【问题标题】:Issue Creating PrivateFontCollection from Resources从资源创建 PrivateFontCollection 的问题
【发布时间】:2016-02-27 03:31:46
【问题描述】:

几个月来我一直在运行同样的代码,它的工作就像一个魅力。最近,我将此代码添加到 TFS 的源代码控制中,但它不再正常工作。唯一的变化是修改了一些命名空间以满足我们的产品指南。目的是使用作为资源嵌入文件中的字体创建 pfc - 原因是可执行文件是可移植的,并且除了安装适当的 .NET 版本之外不依赖任何东西。

    public static Dictionary<string, object> FontDict = new Dictionary<string, object>();

    public static PrivateFontCollection s_FontCollection = new PrivateFontCollection();
    public static Font staticFont;

    public static FontFamily[] FontFamilies
    {
        get
        {
            if (s_FontCollection.Families.Length == 0)
                LoadFonts();

            return s_FontCollection.Families;
        }
    }

    public static Font GetFont(string family, float size)
    {
        foreach (FontFamily font in FontFamilies)
        {
            if (font.Name.ToLower().Equals(family.ToLower()))
            {
                Font ret = new Font(font, size);
                //return (Font)ret.Clone();
                return ret;
            }
        }

        return null;
    }

    public static void LoadFonts()
    {
        if (Assembly.GetEntryAssembly() == null || Assembly.GetEntryAssembly().GetManifestResourceNames() == null)
            return;

        foreach (string resource in Assembly.GetEntryAssembly().GetManifestResourceNames())
        {
            // Load TTF files from your Fonts resource folder.
            if (resource.Contains(".Fonts.") && resource.ToLower().EndsWith(".ttf"))
            {
                using (Stream stream = Assembly.GetEntryAssembly().GetManifestResourceStream(resource))
                {
                    try
                    {
                        // create an unsafe memory block for the font data
                        System.IntPtr data = Marshal.AllocCoTaskMem((int)stream.Length);

                        // create a buffer to read in to
                        byte[] fontdata = new byte[stream.Length];

                        // read the font data from the resource
                        stream.Read(fontdata, 0, (int)stream.Length);

                        // copy the bytes to the unsafe memory block
                        Marshal.Copy(fontdata, 0, data, (int)stream.Length);

                        // pass the font to the font collection
                        s_FontCollection.AddMemoryFont(data, (int)stream.Length);

                        // close the resource stream
                        stream.Close();

                        // free up the unsafe memory
                        Marshal.FreeCoTaskMem(data);
                    }
                    catch
                    {

                    }
                }
            }
        }

        FontDict.Clear();
        FontDict.Add("SYS",    new Font("Arial", 20));
        FontDict.Add("SYSs",   new Font("Arial", 14));
        FontDict.Add("MICR",   GetFont("MICR", 18));                    // Preferred MICR font
        FontDict.Add("SIG",    GetFont("PWSignaturetwo", 30));
        FontDict.Add("HAND1l", GetFont("Daniel", 22));
        FontDict.Add("HAND2l", GetFont("Jenna Sue", 32));
        FontDict.Add("HAND3l", GetFont("Honey Script", 30));
        FontDict.Add("HAND4l", GetFont("Confessions", 42));
        FontDict.Add("HAND5l", GetFont("Soljik-Dambaek", 26));
        FontDict.Add("HAND6l", GetFont("Billy's Hand Thin", 38));
        FontDict.Add("HAND7l", GetFont("Daisy Script", 34));
        FontDict.Add("HAND8l", GetFont("Fineliner SCript", 34));
        FontDict.Add("HAND9l", GetFont("Graphe", 20));
        FontDict.Add("HAND1s", GetFont("Daniel", 16));
        FontDict.Add("HAND2s", GetFont("Jenna Sue", 26));
        FontDict.Add("HAND3s", GetFont("Honey Script", 24));
        FontDict.Add("HAND4s", GetFont("Confessions", 30));
        FontDict.Add("HAND5s", GetFont("Soljik-Dambaek", 20));
        FontDict.Add("HAND6s", GetFont("Billy's Hand Thin", 26));
        FontDict.Add("HAND7s", GetFont("Daisy Script", 26));
        FontDict.Add("HAND8s", GetFont("Fineliner Script", 26));
        FontDict.Add("HAND9s", GetFont("Graphe", 14));
        FontDict.Add("HACKs",  GetFont("Hack", 10));                    //******************************************************//
        FontDict.Add("HACKm",  GetFont("Hack", 12));                    //    Preferred fixed-width font for non-check data     //
        FontDict.Add("HACKl",  GetFont("Hack", 16));                    //******************************************************//

突然发生的事情是所有字体都没有加载到字典中。据我在调试时所说,资源很好。它正确循环所有字体资源。然而,当它到达s_FontCollection.AddMemoryFont 时,它开始跳过一些。我试图弄清楚如何从方法 (as noted here) 中读取 Status 返回值,但似乎无法使其工作。老实说,我没有更改与此相关的其他代码。

真正奇怪的是,这是执行时的问题。我可以多次运行相同的构建,并最终得到关于可用字体的不同结果。

【问题讨论】:

  • 当然,它有一颗随时可以引爆的定时炸弹。今天是你的幸运日,调用 Marshal.FreeCoTaskMem() 是一个错误。您必须保留分配的内存,直到您不能再使用该字体。
  • 我想知道这一点,但我的理解是,一旦字体字节在集合中,它就不再需要指针了。奇怪的是,这以前从未成为问题。有没有更好的方法从嵌入式资源创建 pfc 以便它们始终可用?
  • PrivateFontCollection 有一个 Dispose() 方法,你应该调用它,然后你可以释放内存。你可能从不调用它,几乎没有人调用它,这很好,因为它很难这样做,但也不要调用 FreeCoTaskMem()。
  • @HansPassant - 这也不能解决问题。这甚至不是使用字体的问题。即使在调用 FreeCoTaskMem() 之前,字体也不会内置到 pfc 中。看到资源,它使用该字体逐步通过 LoadFont,到达 AddMemoryFont() 并且不将其添加到 pfc。但不总是。而且并不总是相同的字体。在可执行文件的一次运行中,如果所有字体都正确输入(即使使用 FreeCoTaskMem()),它们在该进程的整个生命周期中始终可用。如果在另一次运行中丢失了一个,它总是丢失。
  • 永远不要使用空的catch 子句,你不知道出了什么问题,我们也不知道。至少捕获异常并使用 Debug.WriteLine() 以便您知道发生了什么。但实际上,您必须完全删除 try/catch,否则您的用户也不知道为什么“它不起作用”。例外是您的朋友,它们会告诉您为什么您的程序不起作用。不要隐藏它们。

标签: c# .net privatefontcollection


【解决方案1】:

原来问题是一个 ttf 文件在添加到源代码管理时损坏。至于上面的讨论,通过 FreeCoTaskMem() 释放内存可以正常工作(就像以前一样),因为它只是暂时需要将字节放入内存,以便可以使用 AddFontFromMem() - 这是嵌入的最简单方法数据进入 pfc。删除错误字体完全解决了这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-09
    • 1970-01-01
    • 2021-02-06
    • 2019-01-12
    • 2020-09-30
    • 2011-02-15
    • 2018-03-06
    相关资源
    最近更新 更多