【问题标题】:Embedded resource font in C# does not work correctlyC# 中的嵌入资源字体无法正常工作
【发布时间】:2016-07-28 21:57:59
【问题描述】:

我在我的资源中嵌入了一个 .ttf 字体文件(特别是“Amatic Bold”),我正在使用下面的代码来获取字体。 我尝试了这篇文章的代码:How do I Embed a font with my C# application? (using Visual Studio 2005)

这是我的实现:

    static public Font GetCustomFont (byte[] fontData, float size, FontStyle style)
    {
        if (_fontCollection == null) _fontCollection = new PrivateFontCollection();
        IntPtr fontPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(fontData.Length);
        System.Runtime.InteropServices.Marshal.Copy(fontData, 0, fontPtr, fontData.Length);
        _fontCollection.AddMemoryFont(fontPtr, fontData.Length);
        System.Runtime.InteropServices.Marshal.FreeCoTaskMem(fontPtr);
        return new Font(_fontCollection.Families[0], size, style);
    }

我就是这样使用它的:

Font font = GetCustomFont(Properties.MainResources.Amatic_Bold, 25, System.Drawing.FontStyle.Bold);     

字体应如下所示:

问题是字体正在加载,但使用时显示不正确;它看起来像“Arial”或其他标准字体,而不是应有的字体。 如果我在 Windows 中安装字体,它可以工作(我想很明显......)

我搜索了一个现有的答案,但找不到我的确切问题...

任何帮助将不胜感激。 提前致谢。

【问题讨论】:

  • 那可怕的漏洞代码就像病毒一样,很难摆脱。您正在被字体保存,它是一种 OpenType 字体。仅可在 WPF 或 Direct2D 应用程序中使用。字体映射器找到可以在 Winforms 应用程序中工作的替代品。
  • 谢谢,@HansPassant。有没有什么好的替代方法可以不用安装字体就可以使用?
  • 安装字体时它真的有效吗?然后修复此代码中的错误。在无法再使用字体并且调用 ._fontCollection.Dispose() 方法之前,您不能调用 Marshal.FreeCoTaskMem() 并且 _fontCollection.Families[0] 总是返回第一个字体,而不是最后加载的字体。
  • 好的,我试试。我知道 _fontCollection.Families[0] 问题,这是我最初计划的行为,尽管我将对其进行修改。再次感谢。
  • Adam,你需要重新组织你的内存管理,所以你只创建一次字体集合,AllocCoTaskMemAddMemoryFont,然后只在窗体/控件时处理字体集合和FreeCoTaskMem处置。如果您尝试在控件上使用字体,则对兼容的文本渲染有一些要求here - 如果这是错误的,字体将被忽略。

标签: c# winforms fonts embedded-resource


【解决方案1】:

那么……我想我明白了!

我将解释我“发现”了什么(无论它是否显而易见):

  • 首先:Application.SetCompatibleTextRenderingDefault 必须设置为 true,以便在控件中呈现内存字体。 (也可以使用Control.UseCompatibleTextRendering) 它在 Microsoft 文档中得到了完美的说明,但我错过了:-(

  • 第二个:PrivateFontCollection.Families 返回一个添加字体的数组,但是.. 惊喜!它是按字母顺序排列的! 无论您添加字体的顺序或使用的方法是什么(AddMemoryFont/AddFontFile),您都会按字母顺序排列! 因此,如果您要添加多个字体,然后尝试获取最后添加的字体,您可能会得到错误的字体。

  • 第三:我也尝试过在集合中添加字体或在表单关闭时执行FreeCoTaskMem()。两者都为我工作! 我不知道这究竟意味着什么......

这是我的最终代码:

    //This list is used to properly dispose PrivateFontCollection after usage
    static private List<PrivateFontCollection> _fontCollections;

    [STAThread]
    private static void Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(true);    //Mandatory in order to have Memory fonts rendered in the controls.

        //Dispose all used PrivateFontCollections when exiting
        Application.ApplicationExit += delegate {
            if (_fontCollections != null) {
                foreach (var fc in _fontCollections) if (fc != null) fc.Dispose();
                _fontCollections = null;
            }
        };

        Application.Run(new frmMain());
    }

    void frmMain_Load(object sender, EventArgs e)
    {
        Font font1 = GetCustomFont(Properties.Resources.Amatic_Bold, 25, FontStyle.Bold);
        //or...
        Font font1 = GetCustomFont("Amatic-Bold.ttf", 25, FontStyle.Bold);

        labelTestFont1.Font = font1;

        Font font2 = GetCustomFont(Properties.Resources.<font_resource>, 25, FontStyle.Bold);
        //or...
        Font font2 = GetCustomFont("<font_filename>", 25, FontStyle.Bold);

        labelTestFont2.Font = font2;

        //...

    }
    static public Font GetCustomFont (byte[] fontData, float size, FontStyle style)
    {
        if (_fontCollections == null) _fontCollections = new List<PrivateFontCollection>();
        PrivateFontCollection fontCol = new PrivateFontCollection();
        IntPtr fontPtr = Marshal.AllocCoTaskMem(fontData.Length);
        Marshal.Copy(fontData, 0, fontPtr, fontData.Length);
        fontCol.AddMemoryFont(fontPtr, fontData.Length);
        Marshal.FreeCoTaskMem(fontPtr);     //<-- It works!
        _fontCollections.Add (fontCol);
        return new Font(fontCol.Families[0], size, style);
    }


    static public Font GetCustomFont (string fontFile, float size, FontStyle style)
    {
        if (_fontCollections == null) _fontCollections = new List<PrivateFontCollection>();
        PrivateFontCollection fontCol = new PrivateFontCollection();
        fontCol.AddFontFile (fontFile);
        _fontCollections.Add (fontCol);
        return new Font(fontCol.Families[0], size, style);
    }

如您所见,我决定为每种字体创建一个专有的 PrivateFontCollection,然后将其存储到一个列表中,以便在应用程序端进行最终处理。

这是在 3 台不同的 PC(Windows 7、32 和 64 位)和 3 种不同的 .ttf 字体中测试的。

结果示例:

我不知道我的方法是否足够好,但我希望它对其他人有用!

更多细节: 与我预期的不同,AddMemoryFont 比 AddFontFile 慢(21ms vs. 15ms)

再次感谢所有 cmets!

【讨论】:

  • 非常感谢,经过这么多小时,Control.UseCompatibleTextRendering 为我解决了使用 AddMemoryFont 的问题!
【解决方案2】:

问题可能是因为在使用字体文件时需要指定 fontfamily 精确字体,编译器切换到默认字体。 您可以从以下两种方法中了解您所缺少的基本概念。

var fontFile = new FontFamily("pack://application:,,,/Resources/#YourFont");
var typeface = new Typeface(new FontFamily(new Uri("pack://application:,,,/"), "/Resources/#YourFont"), FontStyles.Normal, FontWeights.Regular, FontStretches.Normal);
var cultureinfo = new CultureInfo("en-us");
var ft = new FormattedText("YourText", cultureinfo, FlowDirection.LeftToRight,
    typeface, 28, Brushes.White)
dc.DrawText(ft, new Point(0,0));

使用资源路径在客户端系统上安装字体。

PrivateFontCollection yourfont = new PrivateFontCollection();
yourfont.AddFontFile("Your font Path");
label1.Font = new Font(yourfont.Families[0], 16, FontStyle.Regular);

【讨论】:

  • 谢谢@Jerin,我试试看。
猜你喜欢
  • 1970-01-01
  • 2012-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-22
  • 1970-01-01
  • 2020-12-05
  • 2013-10-09
相关资源
最近更新 更多