WPF:如何从 Fonts.SystemFontFamilies 中过滤掉非罗马字体?

标签 wpf fonts

我知道如何使用几行 XAML 创建 WPF 字体选择器,绑定(bind)到 Fonts.SystemFontFamilies ( thanks to Norris Cheng's excellent blog post ),但我不知道如何过滤掉所有国际和其他非罗马字母字体系列。我的用户不太可能需要“Mongolian Baiti”、“Microsoft Uighur”甚至“Webdings”,当它们在列表中时,他们就更难找到他们想要的字体 想。

SystemFontFamilies 访问的对象是否有任何属性?我可以用它来将非符号罗马字母字体系列与其他字体系列分开吗?

编辑:我想使用的信息可在 Windows 7 字体控制面板的“专为”字段中找到。此字段中的此字符串包含“Latin”(用于拉丁字母应用程序的字体)、“international”字体的其他语言名称(例如,“Mongolian Baiti”是“Mongolian”),以及用于 Wingdings 等字体的“Symbol”。 “类别”字段对于将“显示”字体与“文本”字体分开看起来也很有用。不幸的是,我想不出任何办法从代码中获取这些信息,我什至想不出它在 OpenType spec 中的位置。 .我的猜测是 OpenType script tagslanguage tags参与其中。

最佳答案

字体中没有明确定义它是否可读为“罗马”的明确信息。

您可以借助字形分析来查看字体涵盖的 Unicode 范围。这可以为您提供有关字体涵盖哪些语言的线索。但是,这也有问题,例如:

  • 默认的 Windows 7 字体是“Segoe UI”。在您的方案中,您会将其视为“罗马”字体吗?这里的问题是,即使您进行字形分析,它也涵盖了拉丁语以及其他 Unicode 范围,例如阿拉伯语和泰语。好的,我们可以包含至少涵盖拉丁语的字体,但是,如果拉丁语范围实际上不像第 3 点中的拉丁语那样可读怎么办?
  • “Mongolian Baiti”的示例包括涵盖基本拉丁语范围的字形,因此可用于渲染“Roman”文本。
  • Webdings 涵盖了拉丁语范围,因此通过分析它可以通过,但它实际上并不包含可读的拉丁字符。

  • 也许可以应用字形分析来缩小范围,但您可能会得到误报。

    更新

    我必须在我自己的应用程序中处理字体列表,所以不能单独留下这个! :)

    实际上可以通过 GlyphTypeface.Symbol 推导出一个字体是否是符号字体。属性(property)(这对我来说是新的)。因此,通过这个和一些字形分析,以下解决方案应该可以解决问题。

    然而,它仍然会找到“Mongolian Baiti”(它是“Baiti”而不是 curry 风格的“Balti”:)),因为它有拉丁字符的字形,所以它仍然是“罗马”字体,具体取决于你如何定义它。事实上,我系统上的所有非符号字体都至少有拉丁字符范围,所以拉丁字形测试实际上并没有排除任何字体。

    您对“蒙古白提”有什么特别的反对意见,您希望如何自动排除它(例如,不使用手动维护的排除列表)?
    [Test]
    public void test()
    {
        var fonts = Fonts.SystemFontFamilies.OrderBy(x => x.ToString());
    
        var latinFonts = fonts.Where(f => 
            f.Source.StartsWith("Global") ||
            (!IsSymbol(f) && HasLatinGlyphs(f)));
    
        latinFonts.ToList().ForEach(Console.WriteLine);
    }
    
    private bool IsSymbol(FontFamily fontFamily)
    {
        GlyphTypeface glyph = GetFirstGlpyhTypeface(fontFamily);
    
        return glyph.Symbol;
    }
    
    private bool HasLatinGlyphs(FontFamily fontFamily)
    {
        GlyphTypeface glyph = GetFirstGlpyhTypeface(fontFamily);
    
        for (int i = 32; i < 127; i++)
        {
            if (!glyph.CharacterToGlyphMap.ContainsKey(i)) return false;
        }
    
        return true;
    }
    
    private GlyphTypeface GetFirstGlpyhTypeface(FontFamily fontFamily)
    {
        Typeface typeface = fontFamily.GetTypefaces().First();
    
        GlyphTypeface glyph;
    
        typeface.TryGetGlyphTypeface(out glyph);
    
        return glyph;
    }
    

    更新 2

    您可以通过包含 Latin Extended-A 的过滤器来尝试根据它们对扩展拉丁字符的支持来过滤掉字体。和 Latin Extended-B范围。过滤Latin Extended-ALatin Extended-B留下很少的字体,但只是过滤Latin Extended-A仍然留下了相当多的字体。它还会自动删除 Mongolian Baiti因为这只支持Latin-1Latin-1 Supplement .

    这种分析是否给出了理想的结果是非常主观的。可以尝试的东西:
    private bool HasLatinGlyphs(FontFamily fontFamily)
    {
        GlyphTypeface glyph = GetFirstGlpyhTypeface(fontFamily);
    
        List<Tuple<int, int>> ranges = new List<Tuple<int, int>>
        {
            new Tuple<int, int>(32, 126),  //Latin-1
            new Tuple<int, int>(160, 255), //Latin-1 Supplement
            new Tuple<int, int>(256, 383), //Latin Extended-A
            new Tuple<int, int>(384, 591), //Latin Extended-B
        };
    
        foreach (Tuple<int, int> range in ranges)
        {
            for (int i = range.Item1; i <= range.Item2; i++)
            {
                if (!glyph.CharacterToGlyphMap.ContainsKey(i)) return false;
            }
        }
    
        return true;
    }
    

    同样,非常主观,但以下将提供支持拉丁字形和国际货币字符子集的字体:
    List<Tuple<int, int>> ranges = new List<Tuple<int, int>>
    {
        new Tuple<int, int>(32, 126),        //Latin-1
        new Tuple<int, int>(0x20A0, 0x20B5), //Currency Symbols (Partial)
    };
    

    更新 3

    除了您的问题,这里还有一个适用于 Windows 7 的版本。它利用了 Window 7 的隐藏字体功能(如@Rick Sladkey 所指出的那样),默认情况下隐藏被认为对当前用户的区域设置无用的字体.它还将排除符号字体:
    [Test]
    public void test()
    {
        var allFonts = Fonts.SystemFontFamilies.OrderBy(x => x.Source);
    
        var filteredFonts = allFonts.Where(f =>
            IsComposite(f) || (!IsSymbol(f) && !IsHidden(f)));
    
        filteredFonts.ToList().ForEach(Console.WriteLine);
    }
    
    private static bool IsComposite(FontFamily fontFamily)
    {
        return fontFamily.Source.StartsWith("Global");
    }
    
    private static bool IsSymbol(FontFamily fontFamily)
    {
        Typeface typeface = fontFamily.GetTypefaces().First();
        GlyphTypeface glyph;
        typeface.TryGetGlyphTypeface(out glyph);
        return glyph.Symbol;
    }
    
    private static bool IsHidden(FontFamily fontFamily)
    {
        const string Key = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Font Management";
        const string Value = "Inactive Fonts";
        RegistryKey key = Registry.CurrentUser.OpenSubKey(Key);
        IEnumerable<string> hiddenFonts = (string[])key.GetValue(Value);
        return hiddenFonts.Contains(fontFamily.Source);
    } 
    

    关于WPF:如何从 Fonts.SystemFontFamilies 中过滤掉非罗马字体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4798370/

    相关文章:

    c# - WPF 窗口生命周期

    javascript - html2canvas 覆盖 css(字体)

    cocoa - 如何对属性 : delegation pass-through from WebView? 的更改做出有意义的 react

    ios - 了解boldSystemFontOfSize :

    wpf - Caliburn Micro 'Enter' 关键事件

    c# - 如何在运行时更改资源字典颜色

    c# - 打印多页xaml控件

    css - 使用 CSS 对全大写字体进行假大写?

    html - 如何使用 Google 的字体更改 Bootstrap 默认字体系列?

    c# - 以 IN 作为 List<int> 的数据集和查询