C# 从资源中添加字体文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15949057/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 18:35:14  来源:igfitidea点击:

addFontFile from Resources

c#winformsvisual-studio

提问by Ladessa

I have added a custom font using below code:

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

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

I added the font file in resources. How do I add with addFontFilefrom resources?

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

采纳答案by KF2

If you included your font in the resources

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

Try this function

试试这个功能

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);
        }
      }
    }

Edited

已编辑

How load resource from assembly:(YourNamespace.file.ttf)

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

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

My solution explorer:

我的解决方案浏览器:

enter image description here

在此处输入图片说明

回答by sky-dev

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;
}

回答by Corrpside

This is the way I do it.

这就是我这样做的方式。

First get your Font.ttf file and using Visual Studio, drag and drop the file to the root folder or resource folder.

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

In Solution Explorer, right-click the file and click properties. Select Build Action = Content. This will show the file in the Application Files under Project Properties > Publish > Application Files. You will see that the file now can be selected (By default it's automatically included).

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

ClickOnce will now copy the file to the StartupPath

ClickOnce 现在会将文件复制到 StartupPath

To use it, follow this sample:

要使用它,请按照以下示例操作:

PrivateFontCollection pfc = new PrivateFontCollection();

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

textBox1.Font = new Font(pfc.Families[0], 18, FontStyle.Regular);

回答by James Westgate

This will load a font into a private font collection, and avoid any object reference and memory runtime errors you may see using the examples above.

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

For performance reasons, we only wanted to load the font once, and keep references to the font for multiple drawing operations between calls. The trick is to ensure the PrivateFontCollectiondoes no go out of scope if you keep a reference to the Fontobject that you have created.

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

Add some static (shared) variables

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

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

Then declare these functions

然后声明这些函数

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

and

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

Use as follows:

使用方法如下:

Dim font = LoadSharedFont("OpenSans-CondBold.ttf", 12, FontStyle.Bold)

回答by Sam Spade

A word of warning: If you value your sanity and it's possible to avoid this by loading this font from a file then do not struggle with packaging them as resources.I am telling you now: it isn't worth it.1

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

The answer

答案

[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);
}

Okay, there is a lot to unpack here, but for those looking for a copy/paste, you're going to have a bad time without the line of code that follows. It must be run before your first form is created and before your first font is loaded:2

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

Application.SetCompatibleTextRenderingDefault(true);

And you can use this code by simply calling the above function like so:

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

AddFont(Properties.Resources.Roboto_Light);

Now, if you're having trouble with this, what you need to deal with is the AddFontMemResourceExfunction, because it's really difficult to use correctly. Basically what this function does is keep track of fonts in memory. It seems that pfc.AddMemoryFontdoesn't actually increment cFontswhich causes it to silently fail to properly load every font after the first one. This counter must be increased by one every time a unique font family is added. Italic and Bold variants of font families that have already been added don't count as new families and the cFontsshould therefore not be incremented for these.

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

In theory, the return value for AddFontMemResourceExis a pointer referencing the location of the number of fonts currently stored in memory. It also seems impossible to get at the actual number, which is why I'm keeping track manually by incrementing cFonts.If you fail to properly increment cFontsthen this method will silently fail. The only way to tell that it failed is to see that the font wasn't properly loaded.

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

Summary

概括

This answer is quite lengthy. It's the only thing I found that works, but the fact that you need to jump through this many hoops to add a font from a resource is absurd. I don't understand why the AddFromMemory function is so broken and I feel confident I must be doing something wrong or misreading something, but this is it. This was a lot of work, and even now it's not the most stable solution when it comes to bold and italic variations of fonts. If you can explain what is going on here and why this is so weird, I would love to hear it. For now, this is the only way I know.

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

Footnotes

脚注

1: Also, it will break the WindowsFormsDesigner. There is no way to set compatibleTextRenderingDefault to true in the WindowsFormsDesigner environment, so you will constantly get errors and text won't render properly. I solved this by putting the font loading in a try{}catch{} and then making the catch just trying to load from a hardcoded path on my own hard drive. Since loading from a file doesn't require any of this mess, that works like a charm.

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

2: This is absolutely ridiculous. In theory, SetCompatibleTextRenderingDefault()sets the defaulttext rendering technique used by controls. It determines whether they should use a legacy text rendering technique or a newer text rendering technique. Since (at least in my case) fonts are being loaded before even the first control has been generated, I have absolutely no idea why this would affect anything. And I know it's not the fonts being legacy fonts or anything because if I load them from a file (which presumably contains the same data) this setting doesn't matter. It makes no sense whatsoever.

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