如何在使用Visual Studio 2005的C#应用程序中嵌入字体?

17

如何在我正在开发的应用程序中嵌入TrueType字体是最佳方式?基本上,当安装在另一台计算机上时,我希望确保特定字体可供我的应用程序使用。我有*.ttf字体文件,只需要一种将其嵌入或在运行应用程序时自动安装的方法。

我需要设置安装程序在安装期间安装字体,还是可以在应用程序运行时动态加载字体?实际上两种方式都不错。

该应用程序是使用.NET 2.0中的C#开发的。

4个回答

15

有时候我尝试使用创建的字体来绘制文字,但没有保留对PrivateFontCollection的引用时,就会抛出异常。 我想这个字体是我的,为什么还需要集合? 我猜测字体使用其中的信息并且它被垃圾回收了或者其他原因导致了这个问题。 - Jon Turner

13

这里是 Will 的答案,翻译成 C#(未经测试):

PrivateFontCollection pfc = new PrivateFontCollection();

using (Stream fontStream = GetType().Assembly.GetManifestResourceStream("Alphd___.ttf"))
{
    if (null == fontStream)
    {
        return;
    }

    int fontStreamLength = (int) fontStream.Length;

    IntPtr data = Marshal.AllocCoTaskMem(fontStreamLength);

    byte[] fontData = new byte[fontStreamLength];
    fontStream.Read(fontData, 0, fontStreamLength);

    Marshal.Copy(fontData, 0, data, fontStreamLength);

    pfc.AddMemoryFont(data, fontStreamLength);

    Marshal.FreeCoTaskMem(data);
}

连同它们的 Paint() 方法一起:

protected void Form1_Paint(object sender, PaintEventArgs e)
{
    bool bold = false;
    bool italic = false;

    e.Graphics.PageUnit = GraphicsUnit.Point;

    using (SolidBrush b = new SolidBrush(Color.Black))
    {
        int y = 5;

        foreach (FontFamily fontFamily in pfc.Families)
        {
            if (fontFamily.IsStyleAvailable(FontStyle.Regular))
            {
                using (Font font = new Font(fontFamily, 32, FontStyle.Regular))
                {
                    e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
                }
                y += 40;
            }
            if (fontFamily.IsStyleAvailable(FontStyle.Bold))
            {
                bold = true;
                using (Font font = new Font(fontFamily, 32, FontStyle.Bold))
                {
                    e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
                }
                y += 40;
            }
            if (fontFamily.IsStyleAvailable(FontStyle.Italic))
            {
                italic = true;
                using (Font font = new Font(fontFamily, 32, FontStyle.Italic))
                {
                    e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
                }
                y += 40;
            }

            if(bold && italic)
            {
                using(Font font = new Font(fontFamily, 32, FontStyle.Bold | FontStyle.Italic))
                {
                    e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
                }
                y += 40;
            }

            using (Font font = new Font(fontFamily, 32, FontStyle.Underline))
            {
                e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
                y += 40;
            }

            using (Font font = new Font(fontFamily, 32, FontStyle.Strikeout))
            {
                e.Graphics.DrawString(font.Name, font, b, 5, y, StringFormat.GenericTypographic);
            }
        }
    }
}

没错,@coffee_machine。我相信这是因为b在using块中声明了,所以可以被移除。 - Chris Doggett
1
实际上,您正在for循环内部处置它。这只适用于第一次迭代。 - coffee_machine
好的发现。这就是我直接翻译别人代码的结果。谢谢,@coffee_machine! - Chris Doggett
1
@ajukraine:这基本上是对某人已删除的VB答案的翻译。我不确定我从哪里得到了Marshal分配的东西,所以我现在无法回答你,但给我一点时间,我会看看能找到什么。 - Chris Doggett
1
@J-Roel:真希望我能记得,这样我就可以告诉你了。我已经有将近4年没有做C#了。 - Chris Doggett
显示剩余4条评论

11

这比看起来要容易得多;您可以将字体作为资源嵌入到应用程序中,并在应用程序的属性命名空间中访问它作为强类型属性。但是,给定的链接应该是一个很好的起点。


对于禁用VB的用户:

将字体作为应用程序资源添加。 将资源命名为MyFontLol。 您可以通过Properties.Resources.MyFontLol访问此资源(作为字节数组)。

我尚未测试以下内容,但似乎可行:

public void LoadMyFontLolKThx()
{
    // get our font and wrap it in a memory stream
    byte[] myFont = Properties.Resources.MyFontLol;
    using (var ms = new MemoryStream(myFont))
    {
        // used to store our font and make it available in our app
        PrivateFontCollection pfc = new PrivateFontCollection();
        // The next call requires a pointer to our memory font
        // I'm doing it this way; not sure what best practice is
        GCHandle handle = GCHandle.Alloc(ms, GCHandleType.Pinned);
        // If Length > int.MaxValue this will throw
        checked
        {
            pfc.AddMemoryFont(
                handle.AddrOfPinnedObject(), (int)ms.Length); 
        }
        var font = new Font(pfc.Families[0],12);

        // use your font here
    }
}

最后一点。PFC将字体存储为GDI+字体。这些与某些表单控件不兼容。从文档中可以看出:

要使用内存字体,必须使用GDI+渲染控件上的文本。使用SetCompatibleTextRenderingDefault方法,并传递true,在应用程序上设置GDI+渲染,或通过将控件的UseCompatibleTextRendering属性设置为true,在单个控件上设置。有些控件无法使用GDI+进行渲染。


我的字体名称是“Lol”。这是你能想到的最好的名字吗? - Anonymous Pi
我可以理解这个方法的名称 LolKThx。 - djones
当我尝试这样做时,将MemoryStream传递给GCHandle.Alloc存在问题 - 异常的要点是不是原始类型对象。 耸肩 - Josh Sutterfield
@Will:问题,当我访问第一个链接时,我收到了恶意网页警告。链接文本为“因为它告诉我”。这是误报吗? - Frank Bryce
@JohnCarpenter它挂了。我打算把它拿出来。顺便说一句,我很喜欢《异形大战铁血战士》。 - user1228

-1

这可能不是最好的方法,但你可以将字体与资源一起包含,然后将其复制到Windows目录中的字体文件夹中,这样行吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接