Android 字体加载

本文详细介绍了Android系统中字体的加载过程,包括Font配置文件的结构,如system_fonts.xml和fallback_fonts.xml,以及字体加载的流程。在zygote初始化时,Typeface类会通过JNI调用skia库加载字体。解析XML文件后,信息存储在gSystemFonts中,进一步创建并保存到FamilyRec链表。此外,文章还讨论了默认字体和字体族的设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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类型

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值