• 通过skia/freetype 解析字体获取fontfamily fontstyle name 支持多语言翻译


    前言

    我们的项目是基于google skia的渲染引擎
    然后skia关于文字处理这块 是用的 freetype + harfbuzz
    不太清楚的可以先了解下这些库的背景,这里就不说了

    怎么解析字体文件获取fontFamily , fontStyle呢?

    1.既然我们引入了skia 就不太单独引入第三方的库了
    就用skia 来解析 fontFamily , fontStyle

    看一段代码

    1. 生成 SkFontStyleSet * ,传入的是字体文件的数据
    #include "include/core/SkStream.h"
    #include "src/core/SkFontDescriptor.h"
    #include "src/ports/SkFontHost_FreeType_common.h"
    #include "src/ports/SkFontMgr_custom.h"
    
    SkFontStyleSet * genSkFontStyleSet (sk_sp<SkData> data){
        static SkTypeface_FreeType::Scanner scanner;
        static auto baseFntMgr = SkFontMgr_Custom::RefDefault();
        auto stream = std::make_unique<SkMemoryStream>(data);
        int numFaces;
    
        if (!scanner.recognizedFont(stream.get(), &numFaces)) {
            //SkDebugf("---- failed to open <%d> as a font\n",stream.get());
            return nullptr;
        }
    
        SkFontStyleSet_Custom *pNewSet = nullptr;
    
        for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
            bool isFixedPitch;
            SkString realname;
            SkFontStyle style = SkFontStyle(); // avoid uninitialized warning
            if (!scanner.scanFont(stream.get(), faceIndex,
                                  &realname, &style, &isFixedPitch, nullptr))
            {
               // SkDebugf("---- failed to open <%d> <%d> as a font\n", stream.get(),faceIndex);
                return nullptr;
            }
    
            if (nullptr == pNewSet) 
                pNewSet = new SkFontStyleSet_Custom(realname);
            auto fontData = std::make_unique<SkFontData>(stream->fork(), faceIndex, nullptr, 0);
            pNewSet->appendTypeface(sk_make_sp<SkTypeface_Stream>(std::move(fontData),
                                                                style, isFixedPitch,
                                                                true, realname));
        }
        return pNewSet;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    1. 通过 SkFontStyleSet 获取字体的 fontfamily 和 fontstyle
    auto set = genSkFontStyleSet(fontData);
    for (auto i = 0; i < set->count(); i++) {
            SkFontStyle fontStyle;
          
            set->getStyle(i, &fontStyle, nullptr);   //这个接口拿的fontStyleName,测试都是空
    
            SkTypeface *typeface = set->createTypeface(i);
            if (nullptr == typeface) {
                continue;
            }
            SkString fontFamily;
            typeface->getFamilyName(&fontFamily);
            if (fontFamily.size() == 0) {
                continue;
            }
            
            SkString postscriptName;
            typeface->getPostScriptName(&postscriptName);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这里注意 我们familyName 和 postscriptName 都可以拿出来
    但是fontStyleName 是空, 任何字体都是空
    skia的bug
    那么就拖拽skia源码的,新增接口通过freetype 把styleName 取出来

    代码如下
    本地测试的demo

    FT_Library lib;
    
    	int error = FT_Init_FreeType(&lib);
    
    	if (error)
    	{
    		return 0;
    	}
    	FT_Face face;
    
    	error = FT_New_Face(lib, "E:/testFont/PingFangTC-Thin.ttf", 0, &face);
    	auto num = face->num_faces;
    	if (error)
    	{
    		return 0;
    	}
    
    	auto styleName = face->style_name;
    	auto familyName = face->family_name;
    	auto postScriptName = FT_Get_Postscript_Name(face);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    封装成我需要的 修改到skia源码中
    \skia\src\ports\SkFontHost_FreeType_common.cpp

    bool SKTypeface_freetypeHelper::getStyleNamesByFontData(sk_sp<SkData> fontData,
                                                            SkTArray<SkString>& styleNames) {
        static bool s_firstInitFlag = true;
    
        static FT_Library lib;
        int error = 0;
        if (s_firstInitFlag) {
            s_firstInitFlag = false;
            error = FT_Init_FreeType(&lib);
        }
    
        if (error) {
            s_firstInitFlag = true;
            return false;
        }
    
        FT_Face face;
        error = FT_New_Memory_Face(lib, fontData->bytes(), fontData->size(), 0, &face);
       
        if (error) {
            return false;
        }
    
        for (auto i = 0; i < face->num_faces; i++) {
            FT_Face face;
            error = FT_New_Memory_Face(lib, fontData->bytes(), fontData->size(), i, &face);
    
            if (error) {
                continue;
            }
    
            if (face->style_name) {
                styleNames.push_back(SkString(face->style_name));
            }
        }
    
        return !styleNames.empty();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    现在就都能正常读取了 一切都搞定了
    但是有的字体人家带翻译 比如中文的字体名字,我们读到的是英文本地化做的不好啊

    怎么读字体的多语言的 fontfamily 和 fontstyle?

    像这个 fontCreator的截图 一个字体好几个翻译都能读到
    在这里插入图片描述

    freetype 不支持 本地语言!
    人家文档都说了很清楚了
    在这里插入图片描述

    skia 支持fontfamily 的多语言

    通过 LocalizedStrings* createFamilyNameIterator() const;
    在这里插入图片描述
    demo 代码

    std::string xxx(sk_sp<SkTypeface> typeface)
    {
        if (nullptr == typeface)
            return std::string();
    
        SkTypeface::LocalizedString localozedString;
        auto                        it = (typeface)->createFamilyNameIterator();
        while (it->next(&localozedString)) { //遍历取出 会比较多 有 en-us ,zh-cn 等等好几个 
            localozedString.fString.c_str(); //fontFamily
            localozedString.fLanguage.c_str(); //language
    		//vector.pusb_back
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    skia 虽然提供了 family的多语言读取 但是 没有style的
    我准备通过harfbuzz 来读,目前还在调研当中
    后续补充…

    harfbuzz 在windows下编译过后 写了个demo测试了下

    在这里插入图片描述

    关键的就是这个hb_ot_name_get_utf8
    参数一个 是language 这个是按照 BCP 47规范表来传字符串

    在这里插入图片描述

    参数还有一个是 数字2 代表啥? hb_ot_name_id
    就是你想读取字体的什么内容
    你看有 copyright 有 family style fullName 等等 如下
    在这里插入图片描述

    这个搞完 跑起来英文的名字可以拿到,中文的就是读取不出来,我用第三方软件 High-Logic FontCreator
    就可以解析到这个中文, 时间原因这个先不继续深研究了 搁置了… 有清楚大佬评论指点一下 多谢

  • 相关阅读:
    浅谈新生代为什么要分三块区域并且比例为什么是8:1:1
    2022年最常见的Python面试题&答案
    【C#】Redis在net core下使用教程
    QT 实现 TCP 客户端服务器代码
    js 时差计算 根据时间戳获取相差时间 几时几分几秒
    Django内置模型查询讲解
    AIDL for HALs实战
    MySQL-HMA 高可用故障切换
    机器视觉公司怎么可能养我这闲人,连软件加密狗都用不起,项目都用盗版,为什么​?
    Day42 尚硅谷JUC——Fork_Join分支合并框架
  • 原文地址:https://blog.csdn.net/weixin_42837024/article/details/126700992