Android 字体加载

2023-10-26

1.      Font配置文件

    位于frameworks/base/data/fonts

    system_fonts.xml  fallback_fonts.xml

 

文件结构

    <family>

        <nameset>

            <name>sans-serif</name>

            <name>arial</name>

            <name>helvetica</name>

            <name>tahoma</name>

            <name>verdana</name>

        </nameset>

        <fileset>

            <file>Roboto-Regular.ttf</file>

            <file>Roboto-Bold.ttf</file>

            <file>Roboto-Italic.ttf</file>

            <file>Roboto-BoldItalic.ttf</file>

        </fileset>

    </family>

 

可以称每一个 family为一个字体族。 包括:正常字体,粗体,斜体,粗体斜体,每个家族包括四种字体,这四种字体不是都必须的。

2.      字体加载

zygote初始化时preloadclass会加载Typeface类,在这个类中会调用jni-->skia 加载字体。

 

(1)一些数据类型,加载字体解析xml文件,保存到这些数据结构中。

 

structFontFamily {                           --->  family
    SkTDArray<const char*>   fNames;        --->   name     ---> gDefaultNames
    SkTDArray<FontFileInfo*>fFontFileArray;     --->  file       ---> gSystemFonts
    int order;
};

 

FontFamily可以理解为字体族,对应一个<family>标签。

SkTDArray<constchar*>   fNames 保存了<name>字段。

SkTDArray<FontFileInfo*>fFontFileArray 保存了<file>。

 

一个SkTDArray<FontFamily*>,可以保存整个xml文件信息。

structFontFileInfo {
    FontFileInfo() : fFileName(NULL),fVariant(SkPaint::kDefault_Variant),
            fLanguage() {
    }
    const char*          fFileName;
    SkPaint::FontVariant fVariant;
    SkLanguage           fLanguage;
};


FontFileInfo包含了三个字段,fFileName代表标签<file>里的字体文件名字;file 可以有属性 variant="elegant" 和 lang="ja",fVariant指属性variant;fLanguage指属性lang。

staticSkTArray<FontInitRec> gSystemFonts;
structFontInitRec {
    const char*          fFileName;
    const char* const*   fNames;    // null-terminated list
    SkPaint::FontVariant fVariant;
    SkLanguage           fLanguage;
};


FontInitRec包含fFileName字体文件名,字体名fNames,及两个属性fVariant,fLanguage。

gSystemFonts 保存的信息与SkTDArray<FontFamily*>相同,也就是整个xml,是把SkTDArray<FontFamily*>又转存为gSystemFonts。loadFontInfoLocked通过遍历SkTDArray<FontFamily*> fontFamilies,将信息保存gSystemFonts,fFileName,fVariant,fLanguage对应fontFamilies 的fontFileInfo。fNames只有第一个<file>才被赋予<nameset>标签名字列表,其余则为null。

 

char**gDefaultNames保存里第一个<family>标签里的<nameset>。

staticFamilyRec* gDefaultFamily 保存里第一个<family>标签里的字体。

staticSkTypeface* gDefaultNormal 默认字库,normal类型


FamilyRec*gFamilyHead = NULL;  gFamilyHead是FamilyRec链表,每个FamilyRec是一个字体族的实例,fFaces[4]即字体族里的4个字体

structFamilyRec {
    FamilyRec* fNext;
    SkTypeface* fFaces[4];
 
    FamilyRec() : fNext(NULL) {
        memset(fFaces, 0, sizeof(fFaces));
    }
};

 

 

(2)字体加载流程

zygote初始化时preloadclass会加载Typeface类,进行初始化。

Typeface.java静态块

   static {
        DEFAULT         = create((String) null, 0);
        DEFAULT_BOLD    = create((String) null, Typeface.BOLD);
        SANS_SERIF      = create("sans-serif", 0);
        SERIF           = create("serif", 0);
        MONOSPACE      = create("monospace", 0);
      
        sDefaults = new Typeface[] {
            DEFAULT,
            DEFAULT_BOLD,
            create((String) null,Typeface.ITALIC),
            create((String) null,Typeface.BOLD_ITALIC),
        };
    }


create->nativeCreate->Typeface_create->

     SkTypeface::CreateFromName->SkFontHost::CreateTypeface->createTypefaceLocked

 

从 SkTypeface::CreateFromName 进入了skia图形库。


loadSystemFontsLocked是createTypefaceLocked最重要的一步,其余的步骤就是查找最合适的字体类型。


staticSkTypeface* createTypefaceLocked(const SkTypeface* familyFace,
        const char familyName[], const void*data, size_t bytelength,
        SkTypeface::Style style) {
    loadSystemFontsLocked();
 
    // clip to legal style bits
    style = (SkTypeface::Style)(style &SkTypeface::kBoldItalic);
 
    SkTypeface* tf = NULL;
 
    if (NULL != familyFace) {
        tf = findTypefaceLocked(familyFace,style);
    } else if (NULL != familyName) {
        tf = findTypefaceLocked(familyName,style);
    }
 
    if (NULL == tf) {
        tf = findBestFaceLocked(gDefaultFamily,style);
    }
 
    // we ref(), since the semantic is toreturn a new instance
    tf->ref();
    return tf;
}

staticvoid initSystemFontsLocked() {
    // check if we've already been called
    if (gDefaultNormal) {
        return;
    }
 
    SkASSERT(gUniqueFontID == 0);
 
    loadFontInfoLocked(); → 解析xml文件,保存到gSystemFonts等数据结构中
 
    SkTypeface* firstInFamily = NULL;
    for (int i = 0; i <gSystemFonts.count(); i++) {
        // if we're the first in a new family,clear firstInFamily
        const char* const* names =gSystemFonts[i].fNames;
        if (names != NULL) {
            firstInFamily = NULL;
        }
 
        bool isFixedWidth;
        SkString name;
        SkTypeface::Style style;
 
        // we expect all the fonts, except the"fallback" fonts
        bool isExpected = (names != gFBNames);
        if(!getNameAndStyle(gSystemFonts[i].fFileName, &name, &style,
                &isFixedWidth, isExpected)){ → 读取ttf字体文件,获取字体的name和sytle
            // We need to increasegUniqueFontID here so that the unique id of
            // each font matches its index ingSystemFonts array, as expected
            // by findUniqueIDLocked.
            sk_atomic_inc(&gUniqueFontID);
            continue;
        }
 
        SkString fullpath;
        getFullPathForSysFonts(&fullpath,gSystemFonts[i].fFileName);
 
        tf = SkNEW_ARGS(FileTypeface, (style,
                    true,  // system-font (cannot delete)
                    datapath.c_str(), //filename
                    isFixedWidth));        → 为每个ttf创建字体类型  FileTypeface
 
        addTypefaceLocked(tf, firstInFamily); →将每个字体FileTypeface放到gFamilyHead中
 
        if (names != NULL) {
            // see if this is one of our fallbackfonts
            if (names == gFBNames) {
                // add to appropriate fallbackchains
                FallbackFontRec fallbackRec;
                fallbackRec.fFontID =tf->uniqueID();
                fallbackRec.fVariant =gSystemFonts[i].fVariant;
                addFallbackFontLocked(fallbackRec,gSystemFonts[i].fLanguage);
            }
 
            firstInFamily = tf;
            FamilyRec* family =findFamilyLocked(tf);
 
            // record the default family ifthis is it
            if (names == gDefaultNames) {
                gDefaultFamily = family;       → gDefaultFamily保存里第一个<family>里的<fileset>
            }
            // add the names to map to thisfamily
            while (*names) {
                addNameLocked(*names, family);
                names += 1;
            }
        }
    }
    finaliseFallbackFontListsLocked();
 
    // do this after all fonts are loaded. Thisis our default font, and it
    // acts as a sentinel so we only executeloadSystemFontsLocked() once
    gDefaultNormal= findBestFaceLocked(gDefaultFamily, SkTypeface::kNormal); → gDefaultNormal 保存第一个<family>里的normal 字体
}
 
staticvoid loadFontInfoLocked() {
    resetFallbackFontListsLocked();
 
    SkTDArray<FontFamily*> fontFamilies;
    getFontFamilies(fontFamilies);  → 解析system_fonts.xml 文件
 
    gSystemFonts.reset();
 
    for (int i = 0; i <fontFamilies.count(); ++i) {
        FontFamily *family = fontFamilies[i];
        for (int j = 0; j <family->fFontFileArray.count(); ++j) {→遍历每个family的字体
            const char* filename =family->fFontFileArray[j]->fFileName;
            if (haveSystemFont(filename)){→  根据文件名判断,如果gSystemFonts 已经保存了,则跳过。
                continue;
            }
 
            FontInitRec fontInfoRecord;
            fontInfoRecord.fFileName =filename;
            fontInfoRecord.fVariant =family->fFontFileArray[j]->fVariant;
            fontInfoRecord.fLanguage =family->fFontFileArray[j]->fLanguage;
            if (j == 0) {
                if (family->fNames.count()== 0) {
                    // Fallback font
                    fontInfoRecord.fNames =(char **)gFBNames;
                } else {
                    SkTDArray<constchar*> names = family->fNames;
                    const char **nameList =(const char**)
                            malloc((names.count() + 1) *sizeof(char*));
                    if (nameList == NULL) {
                        // shouldn't get here
                        break;
                    }
                    if (gDefaultNames == NULL){
                        gDefaultNames = (char**)nameList; →  gDefaultNames指向第一个family的名字
                    }
                    for (int i = 0; i <names.count(); ++i) {
                        nameList[i] = names[i];
                    }
                    nameList[names.count()] = NULL;
                    fontInfoRecord.fNames =nameList;
                }
            } else {
                fontInfoRecord.fNames = NULL; →除了family的第一个字体,其他的fNames都是null
            }
            gSystemFonts.push_back(fontInfoRecord);→ 将每一个family保存到 gSystemFonts
        }
    }
    fontFamilies.deleteAll();
 
}


 

 

loadSystemFontsLocked->initSystemFontsLocked

loadFontInfoLocked解析 system_fonts.xmlfallback_fonts.xml,保存到gSystemFonts。


    getFontFamilies

        从/system/etc/system_fonts.xml读取字体信息,并加载字体信息,保存到 SkTDArray<FontFamily*> fontFamilies;

        然后遍历 fontFamilies,将数据保存到gSystemFonts。


    gDefaultNames保存里第一个<family>标签里的<nameset>。

 

    SkNEW_ARGS:创建每种字体FileTypeface

    addTypefaceLocked:把创建的字体保存到gFamilyHead链表,gFamilyHead每个节点保存了一个字体family,包含常规、粗体、斜体、粗斜体4种类型。

 

    gdefaultFamily 保存里第一个<family>标签里的字体。

   

    gdefaultNormal 默认字库normal类型

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 字体加载 的相关文章

随机推荐

  • 第二十四章、containers容器类部件QScrollArea滚动区域详解

    老猿Python博文目录 专栏 使用PyQt开发图形界面Python应用 老猿Python博客地址 一 概述 容器部件就是可以在部件内放置其他部件的部件 在Qt Designer中可以使用的容器部件有如下 容器部件中的Scroll Area
  • httpClient,Certificate for ip/域名 doesn't match any of the subject altinative names: []问题处理

    在做https双向认证通信类开发的时候 遇到一个问题 错误信息是 javax net ssl SSLPeerUnverifiedException Certificate for lt 我请求的IP地址 gt doesn t match a
  • 一个比window.open() 打开更快的方法

  • 华为OD机试 - 数列描述(Java)

    题目描述 有一个数列a N N 60 从a 0 开始 每一项都是一个数字 数列中a n 1 都是a n 的描述 其中a 0 1 规则如下 a 0 1 a 1 11 含义 其前一项a 0 1是1个1 即 11 表示a 0 从左到右 连续出现了
  • vulnstack域环境靶场(四)

    环境搭建 进入Ubuntu的web虚拟机 ubuntu为我们的web环境 其中的web环境需要手动开启 全部为docker环境 需要启动的环境分别为 s2 045 CVE 2017 12615 cve 2018 12613 启动方法如下 s
  • 32位算术逻辑运算单元alu设计_算术移位和逻辑移位详解

    大部分的C编译器 用移位的方法得到代码比调用乘除法子程序生成的代码效率高 最近在看Java源码的时候 看到了一些位运算的操作 特意在整理了一下移位运算 不整理不知道 一整理吓一跳 那就随着一起看下去吧 移位运算是将数值向左向右移动 对于十进
  • Centos 6.5下C连接MySQL测试

    Centos 6 5下C连接MySQL测试 include
  • Spark sql的简单使用

    目录 加载依赖 spark sql简单入门 Spark sql简单应用 应用配置 读取文件 读取文本文件并展示数据 show 将数据完全显示 读取json文件 读取jdbc文件 读取压缩格式的文件 将数据以压缩格式存储 parquet或者o
  • hdlc协议解码的四种方法

    hdlc协议规定了 在hdlc中不能出现连续的6个1 因为这是hdlc帧的分割标志 因此在编码的时候每遇到5个连续的1就插入0 因此解码时就需要碰到5个连续的1就要去掉后面的0 而在我们的计算机中都是以整数字节描述信息 因此造成了用pc解h
  • 【uni-app教程】三、 UniAPP 生命周期

    三 UniAPP 生命周期 学习一个工具的目的核心是什么 是为了解决核心业务逻辑问题 业务逻辑很多时候简单的解释一句话 在合适的时机干合适的事情 OK 什么是合适的时机呢 简单的说 页面话行过程中 各个阶段的回调孟数就是页面中的时机 我们也
  • 如何在C语言中巧妙地避免使用if语句?

    近期在做CSAPP的homework时碰到一些题目 代码中不允许使用if语句 于是聪明的同学们想出了一个巧妙的办法 利用C语言中与运算符 的 短路 特性完成了这个任务 例如 如果C语言原语句是 if x gt y a x 为了去掉这个if
  • yolov4 darknet安装笔记

    yolov4 darknet安装笔记 cuda cudnn安装 cuda 10 0下载及安装 运行安装包 运行补丁包 建立软链接 添加环境变量 对应版本cudnn下载及安装 darknet GPU版本安装 Makefile修改 nvcc 路
  • LA@二次型标准形@标准化问题介绍和合同对角化@二次型可标准化定理

    文章目录 二次型的标准形 标准形的矩阵式 标准化问题 合同对角化 二次型标准化分析 二次型可标准化定理 正交相似角度证明 配方角度证明 case1 方法1 case2 方法2 case2 case3 二次型的标准形 如果二次型只含有变量的平
  • Kerberos安全认证-连载12-Kafka Kerberos安全配置及访问

    目录 1 Kafka配置Kerberos 2 客户端操作Kafka 3 Java API操作Kafka 4 StructuredStreaming操作Kafka 5 Flink 操作Kafka 技术连载系列 前面内容请参考前面连载11内容
  • Dictionary的基本用法

    1 创建泛型哈希表 然后加入元素 Dictionary
  • 宋分题——Java实现登录窗口 和 信息录入窗口

    编写一个登录窗口 密码输入采用密码框 输入密码显示为 当输入用户名admin密码123的时候点击确定跳转到学生信息录入窗口界面 其他输入显示用户名密码错误 点击取消退出运行 import java awt import java awt e
  • RANSAC基本原理

    计算机视觉基本原理 RANSAC 1 RANSAC简介 2 基本思想 3 范例 4 迭代次数推导 Reference 1 计算机视觉基本原理 RANSAC 1 RANSAC简介 RANSAC RAndom SAmple Consensus
  • Ubuntu 怎么开放端口

    要在 Ubuntu 中开放端口 需要使用 ufw 防火墙 首先 确保 ufw 已经安装 如果尚未安装 可以使用以下命令进行安装 sudo apt get install ufw 然后 使用以下命令开启 ufw 防火墙 sudo ufwena
  • 玩转代码

    前言 在面试的时候 经常会遇到一道经典的面试题 如何优化网页加载速度 常规的回答中总会有一条 把 css 文件放在页面顶部 把 js 文件放在页面底部 那么 为什么要把 js 文件放在页面的最底部呢 我们先来看下这段代码
  • Android 字体加载

    1 Font配置文件 位于frameworks base data fonts system fonts xml fallback fonts xml 文件结构