C# WinForm使用新字型

前幾天突然有個問題,以前如果我們需要一個新的字型要使用,我們會將下載回來的字型檔安裝到字型裡面後才繼續使用,但是問題來了,如果我寫了一支程式然後那支程式裡面需要使用到非系統預設的新字型,那麼我們就必須要想辦法讓動態的載入或者安裝字型,因此我上網搜尋了一下整理出以下幾種方式可以進行。
動態改變字體目前我知道的共有以下幾種方式 :
1. 使用系統內建或者手動安裝後的字體,但是此種方式有個比較不方便的地方就是每台電腦都必須手動安裝新字型才可正常使用,因此較不建議使用。
2. 載入實體ttf檔直接使用的方式,以下會進行說明。
3. 使用程式自動安裝後使用的字體,以下會進行說明。
4. 動態載入記憶體中使用,以下會進行說明,但此項目我實地進行後還是有些問題存在,因此也不建議使用,不過可能是我自己操作的問題,網路上有人進行這種方式的說明,有興趣的請自行參考調整。


載入實體ttf檔
//載入自訂字型ttf檔
//PrivateFontCollection 的AddFontFile()可把ttf檔直接讀入使用
PrivateFontCollection fontcollection = new PrivateFontCollection();
fontcollection.AddFontFile(Application.StartupPath + "\\Fonts\\cristal.ttf");
Font font = new Font(fontcollection.Families[0], 20);
this.label4.Font = font;
fontcollection.Dispose();


使用程式自動安裝這個部分比較麻煩,因為我發現如果等到程式要用到的時候在安裝一切都來不及了,因此我的作法是在程式進入點(Main),也就是Program.cs裡面執行安裝的動作,另外我也發現如果只有安裝字型在NET裡面似乎還是無法使用,所以還必須注意一下要順便註冊一下註冊檔。還有一點關於字型的名稱要特別注意一下不可以隨便使用,我就發生了一個奇怪的事情,實體檔案名稱就做UnidreamLED.ttf,因此一開始我沒注意到就用UnidreamLED來使用,可是程式一直跟我說該字型不存在,找到最後發現就算檔案亂取,可是字型的名稱(大小寫也有區分)可是要正確不然會造成無法正常使用的,正確的名稱必須如下圖紅框所示。

///  應用程式的主要進入點。 
[STAThread]
static void Main()
{
    InstallFont();

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

//using System.Runtime.InteropServices;
[DllImport("gdi32", EntryPoint = "AddFontResource")]
public static extern int AddFontResourceA(string lpFileName);
[DllImport("gdi32", EntryPoint = "RemoveFontResource")]
public static extern int RemoveFontResourceA(string lpFileName);

private static void InstallFont()
{
    //要事先先加入Regedit
    string fontPath = Application.StartupPath + "\\Fonts\\UniDreamLED.ttf";

    string winFontPath = Environment.GetEnvironmentVariable("WinDir") + "\\Fonts\\UniDreamLED.ttf";

    //File.Delete(winFontPath);
    if (!File.Exists(winFontPath))
    {
        File.Copy(fontPath, winFontPath);
    }

    int result = -1;
    result = RemoveFontResourceA(winFontPath);
    //MessageBox.Show((result == 0) ? "Font was not found." : "Font removed successfully.");
    result = AddFontResourceA(winFontPath);
    //MessageBox.Show((result == 0) ? "Font is already installed." : "Font installed successfully.");

    //註冊Registry
    Registry.SetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 
        "UniDreamLED", 
        "UniDreamLED.ttf", 
        RegistryValueKind.String);
}

//以上執行完後就可以當作系統字型來正常使用了
this.label6.Font = new Font(new FontFamily("UniDreamLED"), 20);


載入記憶體字體注意一下讀取資源檔的檔案格式,正確的格式如下
方案名稱.資料夾(有幾個資料夾就要點幾層).檔案名.副檔名
例如, 如果方案叫ChangeFonts,檔案叫Hostil_Trash.ttf並位於方案根目錄/Font,那麼檔案格式應該是:ChangeFonts.Fonts.Hostil_Trash.ttf。
以下範例是依照麥克斯所提供的方式完成的,但仍有些問題,我就不處理了,有興趣的自己調整一下。

//載入記憶體字體
Assembly l_library = Assembly.GetExecutingAssembly();

PrivateFontCollection privateFontCollection = new PrivateFontCollection();
Stream fontStream = l_library.GetManifestResourceStream("ChangeFonts.Fonts.Hostil_Trash.ttf");
FontFamily fontFamily = LoadFontFamily(fontStream, out privateFontCollection);
Font font1 = new Font(fontFamily, 20);

this.label8.Font = font1;
privateFontCollection.Dispose();

#region 載入記憶體執行 Methods

public static FontFamily LoadFontFamily(Stream stream, out PrivateFontCollection fontCollection)
{
    byte[] buffer = new byte[stream.Length];
    stream.Read(buffer, 0, buffer.Length);
    return LoadFontFamilyUnsafe(buffer, out fontCollection);
}

/*
靜態方法LoadFontFamily()會從一個資料流讀取字型,把字型加到一個PrivateFontCollection.
靜態方法LoadFontFamilyUnsafe()是一個不安全方法,它會去取得一塊固定位置記憶體,把資料流裡的東西讀進去.
使用完該資源後要記得解放掉,你需要明確地呼叫dispose().而且包含不安全程式碼的方案要用unsafe引數去建置.
在方案屬性設定裡->建置->勾選容許Unsafe程式碼.
如果你有其他使用到Unsafe和fixed的情況,請告訴我,謝謝.
*/

public static unsafe FontFamily LoadFontFamilyUnsafe(byte[] buffer, out PrivateFontCollection fontCollection)
{
    fixed (byte* ptr = buffer)
    {
        fontCollection = new PrivateFontCollection();
        fontCollection.AddMemoryFont(new IntPtr(ptr), buffer.Length);
        return fontCollection.Families[0];
    }
}

#endregion


另外微軟有提供將字型與應用程式一起封裝,有興趣者請自行參考MSDN若需要範例程式的解壓縮密碼請留言給我,謝謝


本文範例 : 
ChangeFonts.zip

留言

  1. Dear Sir:
    因為需要到使用新字型的安裝技術
    所以搜尋到您的網頁
    是否可以跟您索取壓縮檔密碼呢?

    謝謝
    小弟信箱dance-finger@hotmail.com

    謝謝
    Summit

    回覆刪除
  2. 我自行建立專案,感覺還是有缺少什麼步驟沒成功。Program.cs都沒問題。還是和你要該檔案的密碼,比對一下。謝謝,我的信箱 min20forever@yahoo.com.tw

    回覆刪除
  3. 請問可以提供壓縮密碼給我嗎?最近剛好在讀這一塊,希望可以做參考~謝謝你。
    t123658314@gmail.com

    回覆刪除
  4. 您好請問還可以提供密碼嗎? 想學習一下如何使用 謝謝您!
    shanksponpon@hotmail.com

    回覆刪除

張貼留言

您好,我是 Lawrence,這裡是我的開發筆記的網誌,如果你對我的文章有任何疑問或者有錯誤的話,歡迎留言讓我知道。