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

McK*_*eG1 7 wpf fonts

我知道如何用几行XAML创建一个WPF字体选择器,绑定到Fonts.SystemFontFamilies(感谢Norris Cheng的优秀博客文章),但我无法弄清楚如何过滤掉所有国际和其他非罗马字母字体家庭.我的用户不太可能需要"蒙Baiti","微软维吾尔",甚至是"Webdings",当那些在名单这使得他们更难找到他们的字体想要的.

SystemFontFamilies我可以用来将非符号罗马字母字体系列与其他字体系列分开,是否可以在对象上找到任何属性?

编辑:我想要使用的信息可以在Windows 7字体控制面板的"Designed for"字段中找到.此字段中的此字符串包含用于拉丁字母应用程序的字体的"拉丁语",用于"国际"字体的其他语言名称(例如,"Mongolian Baiti"是"蒙古语"),以及用于Wingdings等字体的"符号"."类别"字段对于将"显示"字体与"文本"字体分开也很有用.不幸的是,我无法弄清楚从代码中获取此信息的任何方法,我甚至无法弄清楚它在OpenType规范中的位置.我的猜测是涉及OpenType 脚本标签语言标签.

Tim*_*oyd 12

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

您可以使用字形分析来查看所覆盖的字体的Unicode范围.这可以为您提供有关字体覆盖的语言的线索.但是,这也存在问题,例如:

  1. 默认的Windows 7字体是" Segoe UI ".在你的计划中你会看到这是一个"罗马"字体?这里的问题是,即使您执行字形分析,它也涵盖拉丁语以及其他Unicode范围,例如阿拉伯语和泰语.好吧,我们可以包含至少涵盖拉丁语的字体,但是,如果拉丁语范围实际上不像拉丁语那样可读,那么该怎么办?
  2. "Mongolian Baiti"的示例包括覆盖基本拉丁语范围的字形,因此可用于呈现"罗马"文本.
  3. Webdings涵盖拉丁语范围,因此通过分析它可以通过,但它实际上不包含可读的拉丁字符.

雕文分析可能适用于缩小范围,但你可能会得到误报.

更新

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

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

然而,它仍然会找到"蒙古语Baiti"(并且它的"Baiti"不像咖喱风格的"Balti":))因为它有拉丁字符的字形所以它仍然是一个"罗马"字体,取决于你如何定义它.事实上,我系统上的所有非符号字体至少具有拉丁字符范围,因此拉丁字形测试实际上并不排除任何字体.

您对"Mongolian Baiti"的特别反对意见是什么?您希望如何自动排除它(例如,不使用手动维护的排除列表)?

[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;
}
Run Code Online (Sandbox Code Playgroud)

更新2

您可以尝试通过包含过滤器Latin Extended-ALatin 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;
}
Run Code Online (Sandbox Code Playgroud)

同样,非常主观,但以下将提供支持拉丁字形的字体加上国际货币字符的子集:

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)
};
Run Code Online (Sandbox Code Playgroud)

更新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);
} 
Run Code Online (Sandbox Code Playgroud)


A.R*_*.R. 1

我认为如果您不亲自提供的话,您永远不会找到此类信息。字体规范本身不提供此信息,所以我想这意味着您不走运。您最好的选择是确定您认为最终用户“可接受”的字体列表。