我们的项目是基于google skia的渲染引擎
然后skia关于文字处理这块 是用的 freetype + harfbuzz
不太清楚的可以先了解下这些库的背景,这里就不说了
1.既然我们引入了skia 就不太单独引入第三方的库了
就用skia 来解析 fontFamily , fontStyle
看一段代码
#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;
}
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);
}
这里注意 我们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);
封装成我需要的 修改到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();
}
现在就都能正常读取了 一切都搞定了
但是有的字体人家带翻译 比如中文的字体名字,我们读到的是英文本地化做的不好啊
像这个 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
}
}
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
就可以解析到这个中文, 时间原因这个先不继续深研究了 搁置了… 有清楚大佬评论指点一下 多谢