从文件系统加载 ttf 字体而不将其嵌入为资源

问题描述 投票:0回答:1

我有一个 Windows 窗体应用程序,我正在尝试使用文件中的字体。具体来说是 B612 字体,但我认为这没有任何区别。

我尝试使用 PrivateFontCollection.AddFontFile(string) 方法加载它。字体应该正确加载,因为我没有收到任何异常,并且我还迭代了集合的字体系列并且字体就在那里。不幸的是,这有一个问题:它不起作用。文本使用后备字体呈现。

我什至尝试运行 Microsoft 的这个示例。不安装字体:

Fall back font

安装字体后:

Installed font

请注意,如果字体文件位于预期目录下。否则程序将无法运行。

上面的内容非常无用,因为我希望用户能够将他们的字体直接放到目录中,因为他们可能没有安装字体的所有管理权限。将字体作为资源嵌入也无济于事,因为它会降低灵活性。

是否可以从文件中加载字体而不安装它? 我正在 .NET Framework 4.8 Windows 10.0.19045 上尝试此操作

附录

是的,我知道我没有发布任何代码。该代码位于我链接的示例中。诚然,互联网上的死链接多于活链接。所以这里是示例的稍微修改版本,其中屏幕截图基于:

    public partial class Form1 : Form
    {
        private FontFamily[] fontFamilies;
        public Form1()
        {
            InitializeComponent();
            PrivateFontCollection privateFontCollection = new PrivateFontCollection();
            string[] ttfFiles = Directory.GetFiles("C:\\ProgramData\\.darssy 3.2\\Resources\\Fonts", "*.ttf");
            foreach (string ttfFile in ttfFiles)
            {
                privateFontCollection.AddFontFile(ttfFile);
            }

            fontFamilies = privateFontCollection.Families;
            Disposed += (_, _) =>
            {
                Console.WriteLine("Disposing");
                privateFontCollection.Dispose();
            };
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            PointF pointF = new PointF(10, 0);
            SolidBrush solidBrush = new SolidBrush(Color.Black);

            int count = 0;

            count = fontFamilies.Length;

            for (int j = 0; j < count; ++j)
            {
                string familyName = fontFamilies[j].Name;

                if (fontFamilies[j].IsStyleAvailable(FontStyle.Regular))
                {
                    Font regFont = new Font(
                        familyName,
                        16,
                        FontStyle.Regular,
                        GraphicsUnit.Pixel);

                    e.Graphics.DrawString(
                        $"{familyName} {FontStyle.Regular}",
                        regFont,
                        solidBrush,
                        pointF);

                    pointF.Y += regFont.Height;
                }
                //so on and so forth for the rest of the styles

                pointF.Y += 10;
            }
        }
    }

我尝试让示例尽可能接近原始示例。我的更改是删除其余字体样式的重复代码,删除说明明显的荒谬注释,并将初始化和处理移至构造函数。

c# .net winforms fonts privatefontcollection
1个回答
0
投票

这个例子非常明确:

传递给 Font 构造函数的第一个参数是字体系列名称(不是 FontFamily 对象,与 Font 构造函数的其他变体的情况一样)

(强调我的)

当你阅读它时,你的想法很酷,这就是我正在寻找的!当然,直到您运行该示例并意识到现实与该示例的声明相去甚远。

然后 dr.null 在他们的评论中指出“看看

这个问答,看看它是否有效”。在该示例中,直接传递 FontFamily;我尝试了一下,当然成功了。只是这不是我想要的,但我稍后会谈到。

我查看了 FontFamily 代码以及它是如何从字符串创建的。它调用

https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-fontfamily-flat,返回结果为 14,映射到“未找到字体系列”。这意味着要么该示例是错误的,要么是 GDI+ 中存在错误。我会犯第一种情况,就像在这个answer中指出的那样:

PrivateFontCollection 在这种情况下可能不太容易使用,因为它不会使字体在您的应用程序中全局可用,但必须用作 Font 的构造函数参数。

必须将引用传递给 FontFamily 使其完全不切实际。我正在从 json 配置文件中读取文件。在那里,我将字体系列作为字符串,并依靠 GDI+“魔法”来解析实际的 FontFamily 对象。更改此不变量将对应用程序产生影响,这可能会超过其好处。

这给我们留下了三个选择:

    告诉用户手动安装字体。
  • 在安装阶段使用 InnoSetup 安装一些被认为“重要”的字体。
  • 咬紧牙关并进行必要的更改以适应私有字体。这包括:
    • 从私有字体集合中创建字体系列列表。
    • 将此列表传递给“解析器”对象,如果未安装字体,该对象将使用此列表作为后备。
    • 修改 json.net 以使用此解析器,而不是直接从文本创建 Font 对象。
我不会将此标记为正确答案,因为我可能错过了一些东西,并且我不想阻止其他人提供自己的答案。

© www.soinside.com 2019 - 2024. All rights reserved.