12

我使用以下代码添加了自定义字体:

PrivateFontCollection pfc = new PrivateFontCollection();
pfc.AddFontFile("C:\\Path To\\YourFont.ttf");
label1.Font = new System.Drawing.Font(pfc.Families[0], 16, FontStyle.Regular);

我在资源中添加了字体文件。如何addFontFile从资源中添加?

4

5 回答 5

12
private static void AddFontFromResource(PrivateFontCollection privateFontCollection, string fontResourceName)
{
    var fontBytes = GetFontResourceBytes(typeof (App).Assembly, fontResourceName);
    var fontData = Marshal.AllocCoTaskMem(fontBytes.Length);
    Marshal.Copy(fontBytes, 0, fontData, fontBytes.Length);
    privateFontCollection.AddMemoryFont(fontData, fontBytes.Length);
    // Marshal.FreeCoTaskMem(fontData);  Nasty bug alert, read the comment
}

private static byte[] GetFontResourceBytes(Assembly assembly, string fontResourceName)
{
    var resourceStream = assembly.GetManifestResourceStream(fontResourceName);
    if (resourceStream == null)
        throw new Exception(string.Format("Unable to find font '{0}' in embedded resources.", fontResourceName));
    var fontBytes = new byte[resourceStream.Length];
    resourceStream.Read(fontBytes, 0, (int)resourceStream.Length);
    resourceStream.Close();
    return fontBytes;
}
于 2014-05-14T15:08:24.793 回答
8

如果您在资源中包含您的字体

试试这个功能

private void AddFontFromMemory()
{
    Stream fontStream = this.GetType().Assembly.GetManifestResourceStream("yourfont.ttf");
 
    byte[] fontdata = new byte[fontStream.Length];
    fontStream.Read(fontdata,0,(int)fontStream.Length);
    fontStream.Close();

    unsafe
    {
        fixed(byte * pFontData = fontdata)
        {
            pfc.AddMemoryFont((System.IntPtr)pFontData,fontdata.Length);
        }
    }
}

已编辑

如何从程序集中加载资源:(YourNamespace.file.ttf)

Stream fontStream = Assembly.GetExecutingAssembly()
 .GetManifestResourceStream("WindowsFormsApplication1.SBADR.TTF");

我的解决方案资源管理器:

在此处输入图像描述

于 2013-04-11T12:42:47.433 回答
4

我就是这样做的。

首先获取您的 Font.ttf 文件并使用 Visual Studio,将文件拖放到根文件夹或资源文件夹。

在解决方案资源管理器中,右键单击该文件,然后单击属性。选择Build Action = Content。这将在项目属性 > 发布 > 应用程序文件下的应用程序文件中显示该文件。您将看到现在可以选择该文件(默认情况下它会自动包含在内)。

ClickOnce 现在会将文件复制到StartupPath

要使用它,请遵循以下示例:

PrivateFontCollection pfc = new PrivateFontCollection();

pfc.AddFontFile(Path.Combine(Application.StartupPath, "font_name.ttf"));

textBox1.Font = new Font(pfc.Families[0], 18, FontStyle.Regular);
于 2014-11-27T06:06:04.890 回答
3

一句警告:如果您重视自己的理智,并且可以通过从文件中加载此字体来避免这种情况,那么不要将它们打包为资源。我现在告诉你:这不值得。1

答案

[DllImport("gdi32.dll")]
private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts);
private static PrivateFontCollection pfc = new PrivateFontCollection();
private static uint cFonts = 0;
private static void AddFont(byte[] fontdata)
{ 
    int fontLength;  System.IntPtr dataPointer;

    //We are going to need a pointer to the font data, so we are generating it here
    dataPointer = Marshal.AllocCoTaskMem(fontdata.Length);
            

    //Copying the fontdata into the memory designated by the pointer
    Marshal.Copy(fontdata, 0, dataPointer, (int)fontdata.Length);

    // Register the font with the system.
    AddFontMemResourceEx(dataPointer, (uint)fontdata.Length, IntPtr.Zero, ref cFonts);

    //Keep track of how many fonts we've added.
    cFonts += 1;

    //Finally, we can actually add the font to our collection
    pfc.AddMemoryFont(dataPointer, (int)fontdata.Length);
}

好的,这里有很多东西要解压,但是对于那些寻找复制/粘贴的人来说,如果没有下面的代码行,你会很痛苦。它必须在创建第一个表单之前和加载第一个字体之前运行:2

Application.SetCompatibleTextRenderingDefault(true);

您可以通过简单地调用上述函数来使用此代码,如下所示:

AddFont(Properties.Resources.Roboto_Light);

现在,如果你遇到这个问题,你需要处理的是 AddFontMemResourceEx函数,因为它真的很难正确使用。基本上这个函数的作用是跟踪内存中的字体。似乎pfc.AddMemoryFont实际上并没有增加cFonts,这导致它在第一个字体之后默默地无法正确加载每个字体。每次添加唯一字体系列时,此计数器必须加一。已经添加的字体系列的斜体和粗体变体不算作新系列,cFonts因此不应为这些增加。

理论上,for的返回值AddFontMemResourceEx是一个指针,它引用当前存储在内存中的字体数量的位置。获得实际数字似乎也是不可能的,这就是为什么我通过递增手动跟踪cFonts.如果您未能正确递增cFonts,那么此方法将静默失败。判断它失败的唯一方法是查看字体没有正确加载。

概括

这个答案很长。这是我发现的唯一可行的方法,但是您需要跳过这么多圈才能从资源中添加字体这一事实是荒谬的。我不明白为什么 AddFromMemory 函数如此损坏,我确信我一定做错了什么或误读了某些东西,但就是这样。这是很多工作,即使是现在,当涉及到字体的粗体和斜体变体时,它也不是最稳定的解决方案。如果你能解释这里发生了什么以及为什么这很奇怪,我很想听听。目前,这是我知道的唯一方法。

脚注

1:另外,它会破坏 WindowsFormsDesigner。在 WindowsFormsDesigner 环境中无法将 compatibleTextRenderingDefault 设置为 true,因此您将不断收到错误并且文本无法正确呈现。我通过将字体加载放在 try{}catch{} 中解决了这个问题,然后尝试从我自己的硬盘驱动器上的硬编码路径加载捕获。由于从文件加载不需要任何这些混乱,这就像一个魅力。

2:这绝对是荒谬的。理论上,SetCompatibleTextRenderingDefault()设置控件使用的默认文本渲染技术。它确定他们应该使用传统的文本呈现技术还是更新的文本呈现技术。由于(至少在我的情况下)字体甚至在第一个控件生成之前就被加载了,我完全不知道为什么这会影响任何事情。而且我知道字体不是旧字体或任何东西,因为如果我从文件(可能包含相同的数据)加载它们,则此设置无关紧要。这没有任何意义。

于 2020-02-23T00:31:25.837 回答
0

这会将字体加载到私有字体集合中,并避免使用上面的示例可能会看到的任何对象引用和内存运行时错误。

出于性能原因,我们只想加载一次字体,并在调用之间为多次绘制操作保留对字体的引用。诀窍是确保如果您保留对您创建的对象PrivateFontCollection的引用,则不会超出范围。Font

添加一些静态(共享)变量

Private Shared _sharedFont As Font
Private Shared _sharedFontCollection As Text.PrivateFontCollection
Private Shared _sharedFontSize As Integer

然后声明这些函数

Private Function LoadSharedFont(ByVal fontName As String, ByVal size As Integer, ByVal style As FontStyle) As Font
    'Check if font name or size has changed, then clear cache
    If size <> _sharedFontSize Then _sharedFont = Nothing

    If _sharedFont Is Nothing Then

        'Make this shared so this variable doesnt go out of scope and is garbage collected
        _sharedFontCollection = New Text.PrivateFontCollection()
        _sharedFont = LoadFont(fontName, size, style)
        _sharedFontSize = size
    End If

    Return _sharedFont
End Function

Private Function LoadFont(ByVal fontName As String, ByVal size As Integer, ByVal style As FontStyle) As Font
    Dim executingAssembly As System.Reflection.Assembly = Reflection.Assembly.GetCallingAssembly()
    Dim myNamespace As String = executingAssembly.GetName().Name.ToString()

    Try
        Using fontstream = executingAssembly.GetManifestResourceStream(myNamespace + "." + fontName)
            Dim fontBytes(CInt(fontstream.Length)) As Byte
            fontstream.Read(fontBytes, 0, CInt(fontstream.Length))

            Dim fontData As System.IntPtr = Marshal.AllocCoTaskMem(fontBytes.Length)
            Marshal.Copy(fontBytes, 0, fontData, fontBytes.Length)
            _sharedFontCollection.AddMemoryFont(fontData, fontBytes.Length)
            Marshal.FreeCoTaskMem(fontData)
        End Using

        Return New Font(_sharedFontCollection.Families(0), size, style)

    Catch ex As Exception
        'An unexpected error has occurred so return a default Font just in case.
        Return New Drawing.Font("Arial", size, FontStyle.Regular)
    End Try

End Function

使用如下:

Dim font = LoadSharedFont("OpenSans-CondBold.ttf", 12, FontStyle.Bold)
于 2017-10-23T09:19:36.620 回答