锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

Flutter 上字体的另类玩法:FontFeature

时间:2023-02-04 00:00:00 高压电阻器5w500m

在以前的 《Flutter 默认文本和字体知识点 和 带你深入理解 Flutter 字体冷知识 介绍了很多 Flutter 关于字体有趣的知识点,本文继续介绍 Flutter 上关于 Text 属性:FontFeature实际上比较 Flutter ,本文可能与前端或设计更密切相关

相信这篇文章绝对是你能看到的 Flutter FontFeature 少数相关资料之一。

什么是 FontFeature简单地说,它是影响字体形状的属性 ,应该是前端的对应领域 font-feature-settings,它有别于 FontFamily ,是用于指定字体内字的形状的一个参数。

如下图所示 frac 分数和 tnum 对比渲染字体库的情况下,表格数字的对比渲染效果可以实现特殊渲染外, Feature 也有特征的意思,所以也可以理解为字体特征。

我们知道 Flutter 默认在 Android 上使用的是 Roboto 字体,而在 iOS 上使用的是 SF 但实际上,字体 Roboto 字体也分为很多类型,比如你查手机 system/fonts 你会发现很多目录都有 Roboto 字体库存。

所以 Roboto 这样的字体库是一个大的字体集,不同 font-weight 其实对应不同 ttf ,比如默认情况下 Roboto 是不支持 font-weight 为 600 的配置

如下图所示,如果我们设置它 w400 - w700weight ,中间可以清楚地看到 500 和 600 其实厚度是一样的,所以在设置 weight 或者设计 UI 在不同的平台上,需要考虑 weight 是否支持预期的效果

回归到 FontFeature 上,那 Roboto 默认支持多少种? features 呢? 答案是 26 其编码如下图所示,运行后效果如下图所示,从日常使用来看, 26 种 Feature 基本满足大部分开发需求。

“c2sc”、 “ccmp”、 “dlig”、 “dnom”、 “frac”、 “liga”、 “lnum”、 “locl”、 “numr”、 “onum”、 “pnum”、 “salt”、 “smcp”、 “ss01”、 “ss02”、 “ss03”、 “ss04”、 “ss05”、 “ss06”、 “ss07”、 “tnum”、 “unic”、 “cpsp”、 “kern”、 “mark”、 “mkmk”

而 iOS 上的 SF pro 默认支持 39 种 Features , 它们的编码如下图所示,运行后效果如下图所示 SF pro 支持的 Features 更多。

“c2sc”、 “calt”、 “case”、 “ccmp”、 “cv01”、 “cv02”、 “cv03”、 “cv04”、 “cv05”、 “cv06”、 “cv07”、 “cv08”、 “cv09”、 “cv10”、 “dnom”、 “frac”、 “liga”、 “locl”、 “numr”、 “pnum”、 “smcp”、 “ss01”、 “ss02”、 “ss03”、 “ss05”、 “ss06”、 “ss07”、 “ss08”、 “ss09”、 “ss12”、 “ss13”、 “ss14”、 “ss15”、 “ss16”、 “ss17”、 “subs”、 “sups”、 “tnum”、 “kern”

因此,并非所有字体都支持它 Features 都一样,比如 iOS 上支持 sups 上标显示和 subs 但是 Android 上的 Roboto 甚至很多第三方字体也不支持 Features 。

同样在 Web 也有各种各样的限制,比如 swsh默认情况下基本不支持浏览器,fwidnlck 不支持 Safari 浏览器等。

有趣的是,在 Flutter Web 渲染文本时有一个模糊的问题#58159,目前还没有官方修复,但是你可以给它 Text 设置任意 FontFeatures 解决这个问题。

模糊通常是因为使用了 canvas 如果标签绘制文本, Text 控件具有 fontFeatures 它将被设置为

渲染以避免问题

最后,如果是对的 FontFeature 感兴趣的朋友可以通过信息深入了解。如果您对字体有任何疑问,请留言讨论。

  • 假如你想了解更多 features 可通过类型 https://en.wikipedia.org/wiki/List_of_typographic_features 了解更多;

  • 如果你支持你使用的字体 features 感兴趣,可以通过 https://wakamaifondue.com 了解更多;

补充内容

基于网民的问题来补充扩展知识,毕竟,这方面的内容并不多

事实上在 dart 对应可以在里面看到 FontWeight 通常被称为字体集中的字体:

名称
Thin w100
Extra w200
Light w300
Normal/regular/plain w400(默认)
Medium w500
Semi-bold w600
Bold w700
Extra-bold- w800
Black 900

因此,如果您对默认字体有疑问,可以在您的手机字体中找到是否有相应的字体,例如,尽管我们说 roboto 没有 600 ,但是如果是 roboto mono 字体集是有 600 的 fontweight,甚至还有 600 斜体: https://fonts.google.com/specimen/Roboto Mono 。

这里可以用 Android Studio 的 Device File Explorer 查看/system/etc/fonts.xml 现在手机的字体编码,右键文件 save as 在电脑上,下图是华为上的 fonts.xml 截图:

您还可以通过以下原始代码获得相应的现状 Android 字体由系统支持 Typeface ,但是这个 Typeface 不是真正的字体名,而是对应的 fonts.xml

protected Map<String, Typeface> getSSystemFontMap() { 
        
    Map<String, Typeface> sSystemFontMap = null;
    try { 
        
        //Typeface typeface = Typeface.class.newInstance();
        Typeface typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
        Field f = Typeface.class.getDeclaredField("sSystemFontMap");
        f.setAccessible(true);
        sSystemFontMap = (Map<String, Typeface>) f.get(typeface);
        for (Map.Entry<String, Typeface> entry : sSystemFontMap.entrySet()) { 
        
            Log.e("FontMap", entry.getKey() + " ---> " + entry.getValue() + "\n");
        }
    } catch (Exception e) { 
        
        e.printStackTrace();
    }
    return sSystemFontMap;
}

private static List<String> getKeyWithValue(Map map, Typeface value) { 
        
    Set set = map.entrySet();
    List<String> arr = new ArrayList<>();
    for (Object obj : set) { 
        
        Map.Entry entry = (Map.Entry) obj;
        if (entry.getValue().equals(value)) { 
        
            String str = (String) entry.getKey();
            arr.add(str);
        }
    }
    return arr;
}

例如前面我们说过 Roboto 没有 w600 , 但是通过输出比对,华为上有 source-sans-pro 是支持 w600

另外注意这是 Flutter 而不是原生,具体实现调用是在 Engine 的 paragraph_skia.ccparagraph_builder_skia.cc 下对应的 setFontFamilies 相关逻辑,当然默认字体库指定在 typography.dart 下就看到,例如 'Roboto''.SF UI Display''.SF UI Text''.AppleSystemUIFont''Segoe UI'

名称
Android,Fuchsia,Linux Roboto
iOS .SF UI Display,.SF UI Text
MacOS .AppleSystemUIFont
Windows Segoe UI

例如:.SF Text 适用于更小的字体;.SF Display 则适用于偏大的字体,我记得分水岭好像是 20pt 左右,不过 SF(San Francisco) 属于动态字体,系统会动态匹配

另外如果你在 Mac 的 Web 上使用 Flutter Web,可以看到指定的是 .AppleSystemUIFont ,而对于 .AppleSystemUIFont 它其实不算是一种字体,而是苹果上字体的一种集合别称:

还有,如果你去看 Flutter 默认自带的 cupertino/context_menu_action.dart ,就可以看到一个有趣的情况:

为了强调和 iOS 上的样式尽量一直,当开发者配置 isDefaultAction == true 时,会强行指定 '.SF UI Text' 并指定为 FontWeight.w600

当然,前面我们说了那么多,主要是针对英文的情况下,而在中文下还是有差异的,之前的文章也介绍过:

  • 默认在 iOS 上:

    • 中文字体:PingFang SC
    • 英文字体:.SF UI Text.SF UI Display
  • 默认在 Android 上:

    • 中文字体:Source Han Sans / Noto
    • 英文字体:Roboto

例如,在苹果上的简体中文其实会是 PingFang SC 字体,对应还有PingFang TCPingFang HK 的繁体集,而关于这个问题在 Flutter 上之前还出现过比较有意思的 bug :

用户在输入拼音时,iOS 会在中文拼音之间添加额外的 unicode \u2006 字符,比如输入 "nihao" ,iOS 系统会在 skia 中添加文字 “ni\u2006hao ”,从而导致字体无效的情况。

当然后续的 #16709 修复了这个问题 ,而在以前的文章我也讲过,当时我遇到了 “Flutter 在 iOS 系统上,系统语言是韩文时,在和中文一起出现会导致字体显示异常" 的问题

解决方法也很简单,就是给 fontFamilyFallback 配置上 ["PingFang SC" , "Heiti SC"] 就可以了,这是因为韩文在苹果手机上使用的应该是 Apple SD Gothic Neo 这样的超集字体库,【广】这个字符在这个字体集上是不存在的,所以就变成了中文的【广】;

所以可以看到,字体相关是一个平时很少会深入接触的东西,但是一旦涉及多语言和绘制,就很容易碰到问题的领域

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章