/* * Copyright (c) 2020-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "font/ui_font_vector.h" #include #include #include #include #include #include "common/typed_text.h" #include "draw/draw_utils.h" #include "font/font_ram_allocator.h" #include "font/ui_font_cache_manager.h" #include "gfx_utils/file.h" #include "gfx_utils/graphic_log.h" #include "graphic_config.h" #include "securec.h" #if defined(ENABLE_MULTI_FONT) && ENABLE_MULTI_FONT #include "font/ui_multi_font_manager.h" #endif #if defined(ENABLE_SHAPING) && ENABLE_SHAPING #include "font/ui_text_shaping.h" #endif namespace OHOS { UIFontVector::UIFontVector() { #ifdef _WIN32 ttfDir_ = _pgmptr; size_t len = ttfDir_.size(); size_t pos = ttfDir_.find_last_of('\\'); if (pos != std::string::npos) { ttfDir_.replace((pos + 1), (len - pos), VECTOR_FONT_DIR); } #else ttfDir_ = VECTOR_FONT_DIR; #endif // _WIN32 ftLibrary_ = nullptr; freeTypeInited_ = ((FT_Init_FreeType(&ftLibrary_) == 0) ? true : false); } UIFontVector::~UIFontVector() { if (freeTypeInited_) { FT_Done_FreeType(ftLibrary_); freeTypeInited_ = false; UnregisterFontInfo(DEFAULT_VECTOR_FONT_FILENAME); } } bool UIFontVector::IsColorEmojiFont(FT_Face& face) { static const uint32_t tag = FT_MAKE_TAG('C', 'B', 'D', 'T'); FT_ULong length = 0; FT_Load_Sfnt_Table(face, tag, 0, nullptr, &length); if (length) { return true; } return false; } int8_t SetupColorFont(FT_Face face, uint8_t fontSize) { if (face->num_fixed_sizes == 0) { return INVALID_RET_VALUE; } FT_Int bestMatch = 0; int32_t diff = MATH_ABS(fontSize - face->available_sizes[0].width); for (int32_t i = 1; i < face->num_fixed_sizes; ++i) { int32_t ndiff = MATH_ABS(fontSize - face->available_sizes[i].width); if (ndiff < diff) { bestMatch = i; diff = ndiff; } } return FT_Select_Size(face, bestMatch); // FT_Match_Size } uint8_t UIFontVector::RegisterFontInfo(const char* ttfName, uint8_t shaping) { if ((ttfName == nullptr) || !freeTypeInited_) { return FONT_INVALID_TTF_ID; } int32_t j = 0; while (j < FONT_ID_MAX) { if ((fontInfo_[j].ttfName != nullptr) && !strncmp(fontInfo_[j].ttfName, ttfName, TTF_NAME_LEN_MAX)) { return j; } else if (fontInfo_[j].ttfName == nullptr) { std::string ttfPath = ttfDir_; ttfPath.append(ttfName); int32_t error = FT_New_Face(ftLibrary_, ttfPath.c_str(), 0, &ftFaces_[j]); if (error != 0) { return FONT_INVALID_TTF_ID; } fontInfo_[j].ttfName = ttfName; fontInfo_[j].shaping = shaping; fontInfo_[j].ttfId = j; fontInfo_[j].ttcIndex = FONT_TTC_MAX; currentFontInfoNum_ = j + 1; if (IsColorEmojiFont(ftFaces_[j])) { fontInfo_[j].fontWeight = BPP_BIT_32; } else { fontInfo_[j].fontWeight = BPP_BIT_8; } #if defined(ENABLE_MULTI_FONT) && ENABLE_MULTI_FONT UIMultiFontManager::GetInstance()->UpdateScript(fontInfo_[j]); #endif return j; } j++; } return FONT_INVALID_TTF_ID; } uint8_t UIFontVector::RegisterFontInfo(const UITextLanguageFontParam* fontsTable, uint8_t num) { if (fontsTable == nullptr) { return FONT_INVALID_TTF_ID; } uint8_t count = 0; for (uint8_t i = 0; i < num; i++) { uint8_t result = RegisterFontInfo(fontsTable[i].ttfName, fontsTable[i].shaping); if (result == FONT_INVALID_TTF_ID) { continue; } count++; } return count; } // Note: when use ttc font file, freetype should export FT_Stream_New/FT_Stream_Free function uint8_t UIFontVector::RegisterTtcFontInfo(const char* ttcName, const TtfInfo* ttfInfo, uint8_t count) { if ((ttcName == nullptr) || !freeTypeInited_) { return FONT_INVALID_TTF_ID; } int32_t i = 0; int32_t error = 0; int32_t ttfIdx = 0; while (i < FONT_TTC_MAX) { if ((ttcInfos_[i].ttcName != nullptr) && !strncmp(ttcInfos_[i].ttcName, ttcName, TTF_NAME_LEN_MAX)) { return i; } else if (ttcInfos_[i].ttcName == nullptr) { std::string ttcPath = ttfDir_; ttcPath.append(ttcName); FT_Open_Args args = {FT_OPEN_PATHNAME, nullptr, 0, const_cast(ttcPath.c_str()), nullptr, nullptr, 0, nullptr}; error = FT_Stream_New(ftLibrary_, &args, &ttcInfos_[i].stream); if (error != 0) { return FONT_INVALID_TTF_ID; } ttcInfos_[i].ttcName = ttcName; args = {FT_OPEN_STREAM, nullptr, 0, nullptr, ttcInfos_[i].stream, nullptr, 0, nullptr}; for (uint8_t j = 0; j < count; j++) { while ((ttfIdx < FONT_ID_MAX) && fontInfo_[ttfIdx].ttfName != nullptr) { ttfIdx++; } if (ttfIdx >= FONT_ID_MAX) { return FONT_INVALID_TTF_ID; } error = FT_Open_Face(ftLibrary_, &args, j, &ftFaces_[ttfIdx]); if (error != 0) { continue; } fontInfo_[ttfIdx].ttfName = ttfInfo[j].ttfName; fontInfo_[ttfIdx].shaping = ttfInfo[j].shaping; fontInfo_[ttfIdx].ttfId = ttfIdx; fontInfo_[ttfIdx].ttfIndex = j; fontInfo_[ttfIdx].ttcIndex = i; if (IsColorEmojiFont(ftFaces_[ttfIdx])) { fontInfo_[ttfIdx].fontWeight = BPP_BIT_32; } else { fontInfo_[ttfIdx].fontWeight = BPP_BIT_8; } #if defined(ENABLE_MULTI_FONT) && ENABLE_MULTI_FONT UIMultiFontManager::GetInstance()->UpdateScript(fontInfo_[ttfIdx]); #endif } return i; } i++; } return FONT_INVALID_TTF_ID; } uint8_t UIFontVector::UnregisterTtcFontInfo(const char* ttcName, const TtfInfo* ttfInfo, uint8_t count) { if (ttcName == nullptr || ttfInfo == nullptr) { return FONT_INVALID_TTF_ID; } uint8_t i = 0; while (i < FONT_TTC_MAX) { if ((ttcInfos_[i].ttcName != nullptr) && !strncmp(ttcInfos_[i].ttcName, ttcName, TTF_NAME_LEN_MAX)) { for (uint8_t j = 0; j < count; j++) { UnregisterFontInfo(ttfInfo[j].ttfName); } FT_Stream_Free(ttcInfos_[i].stream, 1); ttcInfos_[i].ttcName = nullptr; ttcInfos_[i].stream = nullptr; return i; } i++; } return FONT_INVALID_TTF_ID; } uint8_t UIFontVector::UnregisterFontInfo(const UITextLanguageFontParam* fontsTable, uint8_t num) { if (fontsTable == nullptr) { return 0; } uint8_t count = 0; for (uint8_t i = 0; i < num; i++) { uint8_t result = UnregisterFontInfo(fontsTable[i].ttfName); if (result == FONT_INVALID_TTF_ID) { return FONT_INVALID_TTF_ID; } count++; } return count; } uint8_t UIFontVector::UnregisterFontInfo(const char* ttfName) { if (ttfName != nullptr) { int32_t i = 0; while (i < FONT_ID_MAX) { if ((fontInfo_[i].ttfName != nullptr) && !strncmp(fontInfo_[i].ttfName, ttfName, TTF_NAME_LEN_MAX)) { fontInfo_[i].ttfName = nullptr; FT_Done_Face(ftFaces_[i]); ftFaces_[i] = nullptr; fontSize_[i] = 0; return static_cast(i); } i++; } } return FONT_INVALID_TTF_ID; } const UITextLanguageFontParam* UIFontVector::GetFontInfo(uint16_t fontId) const { if (fontId < FONT_ID_MAX) { return static_cast(&fontInfo_[fontId]); } return nullptr; } int32_t UIFontVector::OpenVectorFont(uint8_t ttfId) { int32_t i = 0; int32_t fp = 0; while (i < FONT_ID_MAX) { if (fontInfo_[i].ttfName == nullptr) { i++; continue; } if (fontInfo_[i].ttfId == ttfId) { std::string ttfPath = ttfDir_; ttfPath.append(fontInfo_[i].ttfName); #ifdef _WIN32 fp = open(ttfPath.c_str(), O_RDONLY | O_BINARY); #else fp = open(ttfPath.c_str(), O_RDONLY); #endif return fp; } i++; } return -1; } bool UIFontVector::GetTtfInfo(uint8_t ttfId, uint8_t* ttfBuffer, uint32_t ttfBufferSize, TtfHeader& ttfHeader) { if ((ttfBuffer == nullptr) || (ttfBufferSize == 0)) { return false; } for (int16_t i = 0; i < FONT_ID_MAX; i++) { if (fontInfo_[i].ttfName == nullptr) { continue; } if (fontInfo_[i].ttfId == ttfId) { if (fontInfo_[i].ttcIndex != FONT_TTC_MAX) { return GetTtfInfoFromTtc(ttfBuffer, ttfBufferSize, ttfHeader, fontInfo_[i]); } else { return GetTtfInfoFromTtf(ttfBuffer, ttfBufferSize, ttfHeader, fontInfo_[i]); } } } return false; } bool UIFontVector::GetTtfInfoFromTtf(uint8_t* ttfBuffer, uint32_t ttfBufferSize, TtfHeader& ttfHeader, UITextLanguageFontParam fontInfo) { if ((ttfBuffer == nullptr) || (ttfBufferSize == 0)) { return false; } std::string ttfPath = ttfDir_; ttfPath.append(fontInfo.ttfName); int32_t fpTtf = 0; #ifdef _WIN32 fpTtf = open(ttfPath.c_str(), O_RDONLY | O_BINARY); #else fpTtf = open(ttfPath.c_str(), O_RDONLY); #endif if (fpTtf < 0) { return false; } int32_t headerLength = lseek(fpTtf, 0, SEEK_END); if (headerLength < 0) { return false; } ttfHeader.len = static_cast(headerLength); if (ttfHeader.len > ttfBufferSize) { close(fpTtf); return false; } int32_t ret = lseek(fpTtf, 0, SEEK_SET); if (ret != 0) { close(fpTtf); return false; } ret = read(fpTtf, reinterpret_cast(ttfBuffer), ttfHeader.len); if (ret != headerLength) { close(fpTtf); return false; } close(fpTtf); return true; } struct TtcHeader { uint32_t ttcTag; uint16_t major; uint16_t minor; int32_t numFonts; }; bool UIFontVector::GetTtfInfoFromTtc(uint8_t* ttfBuffer, uint32_t ttfBufferSize, TtfHeader& ttfHeader, UITextLanguageFontParam fontInfo) { if ((ttfBuffer == nullptr) || (ttfBufferSize == 0) || (fontInfo.ttcIndex >= FONT_TTC_MAX)) { return false; } FT_Stream stream = ttcInfos_[fontInfo.ttcIndex].stream; if (stream == nullptr) { return false; } FT_Error error = FT_Err_Ok; if (FT_STREAM_SEEK(0)) { return false; } // read ttc header TtcHeader header = {}; static const FT_Frame_Field ttcHeaderFields[] = { #undef FT_STRUCTURE #define FT_STRUCTURE TtcHeader FT_FRAME_START(12), // 12: see ttc header FT_FRAME_ULONG(ttcTag), FT_FRAME_LONG(numFonts), FT_FRAME_END}; if (FT_STREAM_READ_FIELDS(ttcHeaderFields, &header)) { return false; } // check if ttc file if (header.ttcTag != TTAG_ttcf) { // 'ttcf' - TTC file return false; } uint8_t ttfIndex = fontInfo.ttfIndex; if (ttfIndex >= header.numFonts) { // invalid index return false; } // change position to the ttf offset if (FT_STREAM_SKIP(4 * ttfIndex)) { // 4: table dictionary offset length return false; } // get the ttf length uint32_t ttfOffset; if (FT_READ_ULONG(ttfOffset)) { return false; } uint32_t ttfLength = 0; FT_ULong ttcLength = stream->size; if (ttcLength < ttfOffset) { return false; } if (ttfIndex + 1 == header.numFonts) { ttfLength = ttcLength - ttfOffset; } else { uint32_t nextTtfOffset; if (FT_READ_ULONG(nextTtfOffset)) { return false; } ttfLength = nextTtfOffset - ttfOffset; } if (ttfLength > ttfBufferSize) { return false; } if (FT_STREAM_SEEK(ttfOffset) || FT_STREAM_READ(ttfBuffer, ttfLength)) { return false; } ttfHeader.len = ttfLength; // read number of tables uint16_t numTables; if (FT_STREAM_SEEK(ttfOffset + 4) || FT_READ_USHORT(numTables)) { // 4: sfntVersion length return false; } // change the offset of the ttf tableRecord compare with ttfOffset from ttc header uint32_t* p = reinterpret_cast(ttfBuffer + 20); // 20: 12(TableDirectory) + 8(tableTag and checksum) for (uint16_t i = 0; i < numTables; i++) { p[0] = FT_PEEK_ULONG(p) - ttfOffset; p[0] = FT_PEEK_ULONG(p); p += 4; // 4: Table Record size } return true; } bool UIFontVector::IsVectorFont() const { return true; } uint8_t UIFontVector::GetFontWeight(uint16_t fontId) { if (fontId >= FONT_ID_MAX) { return BPP_BIT_8; } return fontInfo_[fontId].fontWeight; } int8_t UIFontVector::SetFontPath(const char* path, FontType type) { if (path == nullptr) { return INVALID_RET_VALUE; } ttfDir_ = path; return RET_VALUE_OK; } int8_t UIFontVector::GetFaceInfo(uint16_t fontId, uint8_t fontSize, FaceInfo& faceInfo) { if ((fontId >= FONT_ID_MAX) || (fontSize == 0)) { return INVALID_RET_VALUE; } faceInfo.key = GetKey(fontId, fontSize); faceInfo.face = ftFaces_[fontId]; if (fontSize_[fontId] == fontSize) { return RET_VALUE_OK; } const UITextLanguageFontParam* fontInfo = GetFontInfo(fontId); if ((fontInfo == nullptr) || (fontInfo->ttfName == nullptr)) { return INVALID_RET_VALUE; } if (!freeTypeInited_) { return INVALID_RET_VALUE; } // Set the size int8_t ret; if (IsEmojiFont(fontId)) { ret = SetupColorFont(ftFaces_[fontId], fontSize); } else { ret = FT_Set_Char_Size(faceInfo.face, fontSize * FONT_PIXEL_IN_POINT, 0, 0, 0); } if (ret != 0) { return INVALID_RET_VALUE; } fontSize_[fontId] = fontSize; return RET_VALUE_OK; } uint16_t UIFontVector::GetHeight(uint16_t fontId, uint8_t fontSize) { FaceInfo faceInfo; int8_t ret = GetFaceInfo(fontId, fontSize, faceInfo); if (ret != RET_VALUE_OK) { return INVALID_RET_VALUE; } if (!freeTypeInited_ || (faceInfo.face == nullptr)) { return 0; } return static_cast(faceInfo.face->size->metrics.height / FONT_PIXEL_IN_POINT); } uint16_t UIFontVector::GetShapingFontId(char* text, uint8_t& ttfId, uint32_t& script, uint16_t fontId, uint8_t size) const { #if defined(ENABLE_MULTI_FONT) && ENABLE_MULTI_FONT const UITextLanguageFontParam* fontParam1 = GetFontInfo(fontId); if (fontParam1 == nullptr) { return 0; } if (fontParam1->shaping == 0) { UIMultiFontManager* multiFontManager = UIMultiFontManager::GetInstance(); if (!multiFontManager->IsNeedShaping(text, ttfId, script)) { return 0; // 0 means no need to shape } uint16_t* searchLists = nullptr; int8_t length = multiFontManager->GetSearchFontList(fontId, &searchLists); const UITextLanguageFontParam* fontParam2 = nullptr; for (uint8_t i = 0; i < length; i++) { fontParam2 = GetFontInfo(searchLists[i]); if (fontParam2 == nullptr) { continue; } if (fontParam2->ttfId == ttfId) { return fontParam2->shaping; } } return 0; } ttfId = fontParam1->ttfId; #if defined(ENABLE_SHAPING) && ENABLE_SHAPING script = UIMultiFontManager::GetInstance()->GetScriptByTtfId(ttfId); #endif return fontParam1->shaping; #else const UITextLanguageFontParam* fontInfo = GetFontInfo(fontId); if (fontInfo == nullptr) { return 0; } ttfId = fontInfo->ttfId; return fontInfo->shaping; #endif } uint16_t UIFontVector::GetFontId(const char* ttfName, uint8_t fontSize) const { if (ttfName != nullptr) { int32_t i = 0; while (i < FONT_ID_MAX) { if ((fontInfo_[i].ttfName != nullptr) && (strstr(fontInfo_[i].ttfName, ttfName) != nullptr)) { return static_cast(i); } i++; } } return static_cast(FONT_ID_MAX); } uint16_t UIFontVector::GetFontId(uint32_t unicode) const { int32_t i = 0; uint8_t ttfId = ((unicode >> 24) & 0x1F); // 24: Whether 25 ~29 bit storage is ttfId 0x1F:5bit while (i < FONT_ID_MAX) { if (fontInfo_[i].ttfName == nullptr) { i++; continue; } if (fontInfo_[i].ttfId == ttfId) { return i; } i++; } return FONT_INVALID_TTF_ID; } int16_t UIFontVector::GetWidth(uint32_t unicode, uint16_t fontId, uint8_t fontSize) { if ((fontId >= FONT_ID_MAX) || (fontSize == 0)) { return INVALID_RET_VALUE; } GlyphNode node; int8_t ret = GetGlyphNode(unicode, node, fontId, fontSize); if (ret != RET_VALUE_OK) { return INVALID_RET_VALUE; } return node.advance; } int8_t UIFontVector::GetFontHeader(FontHeader& fontHeader, uint16_t fontId, uint8_t fontSize) { FaceInfo faceInfo; int8_t ret = GetFaceInfo(fontId, fontSize, faceInfo); if (ret != RET_VALUE_OK) { return INVALID_RET_VALUE; } if (!freeTypeInited_ || (faceInfo.face == nullptr)) { return INVALID_RET_VALUE; } fontHeader.ascender = static_cast(faceInfo.face->size->metrics.ascender / FONT_PIXEL_IN_POINT); fontHeader.descender = static_cast(faceInfo.face->size->metrics.descender / FONT_PIXEL_IN_POINT); fontHeader.fontHeight = static_cast(faceInfo.face->size->metrics.height / FONT_PIXEL_IN_POINT); return RET_VALUE_OK; } void UIFontVector::SaveGlyphNode(uint32_t unicode, uint16_t fontKey, Metric *metric) { GlyphCacheNode* node = UIFontCacheManager::GetInstance()->GetNodeCacheSpace(unicode, fontKey); if (node == nullptr) { return; } node->node.left = metric->left; node->node.top = metric->top; node->node.cols = metric->cols; node->node.rows = metric->rows; node->node.advance = metric->advance; node->node.unicode = unicode; node->node.fontId = fontKey; } int8_t UIFontVector::GetGlyphNode(uint32_t unicode, GlyphNode& glyphNode, uint16_t fontId, uint8_t fontSize) { // get glyph from glyph cache uint16_t fontKey = GetKey(fontId, fontSize); UIFontCacheManager* fontCacheManager = UIFontCacheManager::GetInstance(); GlyphCacheNode* cacheNode = fontCacheManager->GetNodeFromCache(unicode, fontKey, GlyphCacheType::CACHE_TYPE_NONE); if (cacheNode != nullptr) { glyphNode = cacheNode->node; return RET_VALUE_OK; } #if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE uint8_t* bitmap = fontCacheManager->GetBitmap(fontKey, unicode, glyphNode.textStyle); #else uint8_t* bitmap = fontCacheManager->GetBitmap(fontKey, unicode, TEXT_STYLE_NORMAL); #endif if (bitmap != nullptr) { Metric* f = reinterpret_cast(bitmap); glyphNode.left = f->left; glyphNode.top = f->top; glyphNode.cols = f->cols; glyphNode.rows = f->rows; glyphNode.advance = f->advance; glyphNode.fontId = fontId; SaveGlyphNode(unicode, fontKey, f); return RET_VALUE_OK; } FaceInfo faceInfo; int8_t ret = GetFaceInfo(fontId, fontSize, faceInfo); if (ret != RET_VALUE_OK) { return INVALID_RET_VALUE; } if (faceInfo.face == nullptr) { return INVALID_RET_VALUE; } int8_t error = LoadGlyphIntoFace(fontId, fontSize, unicode, glyphNode); if (error != RET_VALUE_OK) { return INVALID_RET_VALUE; } return RET_VALUE_OK; } uint8_t* UIFontVector::GetBitmap(uint32_t unicode, GlyphNode& glyphNode, uint16_t fontId, uint8_t fontSize) { uint16_t fontKey = GetKey(fontId, fontSize); UIFontCacheManager* fontCacheManager = UIFontCacheManager::GetInstance(); #if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE uint8_t* bitmap = fontCacheManager->GetBitmap(fontKey, unicode, glyphNode.textStyle); #else uint8_t* bitmap = fontCacheManager->GetBitmap(fontKey, unicode, TEXT_STYLE_NORMAL); #endif if (bitmap != nullptr) { Metric* f = reinterpret_cast(bitmap); glyphNode.left = f->left; glyphNode.top = f->top; glyphNode.cols = f->cols; glyphNode.rows = f->rows; glyphNode.advance = f->advance; glyphNode.fontId = fontId; SaveGlyphNode(unicode, fontKey, f); return bitmap + sizeof(Metric); } FaceInfo faceInfo; int8_t ret = GetFaceInfo(fontId, fontSize, faceInfo); if (ret != RET_VALUE_OK) { return nullptr; } if (faceInfo.face == nullptr) { return nullptr; } int8_t error = LoadGlyphIntoFace(fontId, fontSize, unicode, glyphNode); if (error != RET_VALUE_OK) { return nullptr; } #if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE bitmap = fontCacheManager->GetBitmap(fontKey, unicode, glyphNode.textStyle); #else bitmap = fontCacheManager->GetBitmap(fontKey, unicode, TEXT_STYLE_NORMAL); #endif if (bitmap != nullptr) { return bitmap + sizeof(Metric); } else { return nullptr; } } bool UIFontVector::IsEmojiFont(uint16_t fontId) { if (fontId >= FONT_ID_MAX) { return false; } return (fontInfo_[fontId].fontWeight >= 16); // 16: rgb color font } #if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE void UIFontVector::SetItaly(FT_GlyphSlot slot) { if (slot->format != FT_GLYPH_FORMAT_OUTLINE) { GRAPHIC_LOGE("SetItaly error"); return; } float lean = 0.2f; // Slope of word FT_Matrix matrix; matrix.xx = 0x10000L; // Staggered matrix along x-axis matrix.xy = lean * 0x10000L; matrix.yx = 0; matrix.yy = 0x10000L; // Staggered matrix along y-axis FT_Outline outline = slot->outline; FT_Outline_Transform(&outline, &matrix); } #endif #if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE void UIFontVector::SetBold(uint16_t fontId) { int32_t error; FT_GlyphSlot slot = ftFaces_[fontId]->glyph; // some reasonable strength, copied from freeType FT_Pos xBold = FT_MulFix(ftFaces_[fontId]->units_per_EM, ftFaces_[fontId]->size->metrics.y_scale) / 24; FT_Pos yBold = xBold; if (ftFaces_[fontId]->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { FT_BBox oldBox; FT_Outline_Get_CBox(&slot->outline, &oldBox); error = FT_Outline_Embolden(&slot->outline, xBold); if (error != 0) { GRAPHIC_LOGE("SetBold error"); return; } } else if (ftFaces_[fontId]->glyph->format == FT_GLYPH_FORMAT_BITMAP) { FT_Library ftLibrary = slot->library; error = FT_Bitmap_Embolden(ftLibrary, &slot->bitmap, xBold, yBold); if (error != 0) { GRAPHIC_LOGE("SetBold error"); return; } } } #endif int8_t UIFontVector::LoadGlyphIntoFace(uint16_t& fontId, uint8_t fontSize, uint32_t unicode, GlyphNode& glyphNode) { int32_t error; if (IsGlyphFont(unicode) != 0) { if (fontId >= FONT_ID_MAX || fontId != GetFontId(unicode)) { return INVALID_RET_VALUE; } error = FT_Load_Glyph(ftFaces_[fontId], unicode & (0xFFFFFF), FT_LOAD_RENDER); } else { if (IsEmojiFont(fontId)) { error = FT_Load_Char(ftFaces_[fontId], unicode, FT_LOAD_COLOR); } else { error = FT_Load_Char(ftFaces_[fontId], unicode, FT_LOAD_RENDER); } } if ((error != 0) || (ftFaces_[fontId]->glyph->glyph_index == 0)) { return INVALID_RET_VALUE; } FaceInfo faceInfo; faceInfo.key = GetKey(fontId, fontSize); faceInfo.face = ftFaces_[fontId]; glyphNode.left = faceInfo.face->glyph->bitmap_left; glyphNode.top = faceInfo.face->glyph->bitmap_top; glyphNode.cols = faceInfo.face->glyph->bitmap.width; glyphNode.rows = faceInfo.face->glyph->bitmap.rows; glyphNode.advance = static_cast(faceInfo.face->glyph->advance.x / FONT_PIXEL_IN_POINT); glyphNode.fontId = fontId; #if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE SetFace(faceInfo, unicode, glyphNode.textStyle); #else SetFace(faceInfo, unicode); #endif return RET_VALUE_OK; } #if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE int8_t UIFontVector::LoadGlyphIntoFace(uint16_t& fontId, uint32_t unicode, FT_Face face, TextStyle textStyle) { int32_t error; if (IsGlyphFont(unicode) != 0) { if (fontId != GetFontId(unicode)) { return INVALID_RET_VALUE; } unicode = unicode & (0xFFFFFF); // Whether 0 ~24 bit storage is unicode error = FT_Load_Glyph(ftFaces_[fontId], unicode, FT_LOAD_RENDER); } else { if (IsEmojiFont(fontId)) { error = FT_Load_Char(ftFaces_[fontId], unicode, FT_LOAD_COLOR); } else { error = FT_Load_Char(ftFaces_[fontId], unicode, FT_LOAD_RENDER); } } if ((error != 0) || (ftFaces_[fontId]->glyph->glyph_index == 0)) { return INVALID_RET_VALUE; } if (textStyle == TEXT_STYLE_ITALIC) { SetItaly(ftFaces_[fontId]->glyph); } else if (textStyle == TEXT_STYLE_BOLD) { SetBold(fontId); } else if (textStyle == TEXT_STYLE_BOLD_ITALIC) { SetItaly(ftFaces_[fontId]->glyph); SetBold(fontId); } if (ftFaces_[fontId]->glyph->format != FT_GLYPH_FORMAT_BITMAP) { error = FT_Render_Glyph(ftFaces_[fontId]->glyph, FT_RENDER_MODE_NORMAL); } if ((error != 0) || (ftFaces_[fontId]->glyph->glyph_index == 0)) { return INVALID_RET_VALUE; } return RET_VALUE_OK; } #endif uint8_t UIFontVector::IsGlyphFont(uint32_t unicode) { uint16_t unicodeFontId = GetFontId(unicode); if (unicodeFontId == FONT_INVALID_TTF_ID) { return 0; } else { return fontInfo_[unicodeFontId].shaping; } } void UIFontVector::SetFace(FaceInfo& faceInfo, uint32_t unicode) { SetFace(faceInfo, unicode, TEXT_STYLE_NORMAL); } void UIFontVector::SetFace(FaceInfo& faceInfo, uint32_t unicode, TextStyle textStyle) { Metric* f = reinterpret_cast(UIMalloc(sizeof(Metric))); if (f == nullptr) { return; } f->advance = static_cast(faceInfo.face->glyph->advance.x / FONT_PIXEL_IN_POINT); f->left = faceInfo.face->glyph->bitmap_left; f->top = faceInfo.face->glyph->bitmap_top; f->cols = faceInfo.face->glyph->bitmap.width; f->rows = faceInfo.face->glyph->bitmap.rows; // cache glyph SaveGlyphNode(unicode, faceInfo.key, f); int16_t pixSize; ColorMode mode; if (faceInfo.face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { pixSize = 0x04; // 4 Byte mode = ARGB8888; } else { pixSize = 1; mode = A8; } GlyphNode glyphNode; glyphNode.left = f->left; glyphNode.top = f->top; glyphNode.cols = f->cols; glyphNode.rows = f->rows; glyphNode.advance = f->advance; glyphNode.unicode = unicode; glyphNode.fontId = faceInfo.key; BufferInfo bufInfo = UIFontAllocator::GetCacheBuffer(faceInfo.key, unicode, mode, glyphNode, true, textStyle); uint32_t bitmapSize = bufInfo.stride * bufInfo.height; uint32_t rawSize = glyphNode.cols * glyphNode.rows * pixSize; if (bufInfo.virAddr != nullptr) { if (memcpy_s(bufInfo.virAddr, sizeof(Metric), f, sizeof(Metric)) != EOK) { UIFontCacheManager::GetInstance()->PutSpace(reinterpret_cast(bufInfo.virAddr)); UIFree(f); return; } if ((faceInfo.face->glyph->bitmap.buffer != nullptr) && (memcpy_s(reinterpret_cast(bufInfo.virAddr) + sizeof(Metric), bitmapSize, faceInfo.face->glyph->bitmap.buffer, rawSize) != EOK)) { UIFontCacheManager::GetInstance()->PutSpace(reinterpret_cast(bufInfo.virAddr)); UIFree(f); return; } UIFontAllocator::RearrangeBitmap(bufInfo, rawSize, true); ClearFontGlyph(faceInfo.face); } UIFree(f); } void UIFontVector::ClearFontGlyph(FT_Face face) { if ((face != nullptr) && (face->glyph != nullptr)) { // free unicode buffer immediately to save memory in multi font file load // if not, it will be freed in next glyph load ft_glyphslot_free_bitmap(face->glyph); FT_Outline_Done(face->glyph->library, &face->glyph->outline); if (face->glyph->internal != nullptr) { FT_GlyphLoader_Reset(face->glyph->internal->loader); } } } inline uint16_t UIFontVector::GetKey(uint16_t fontId, uint8_t size) { return ((static_cast(fontId)) << 8) + size; // fontId store at the (8+1)th bit } uint16_t UIFontVector::GetOffsetPosY(const char* text, uint16_t lineLength, bool& isEmojiLarge, uint16_t fontId, uint8_t fontSize) { if (!freeTypeInited_) { return INVALID_RET_VALUE; } uint32_t i = 0; uint16_t textNum = 0; uint16_t emojiNum = 0; uint16_t loopNum = 0; GlyphNode glyphNode; GlyphNode emojiMaxNode = {}; uint8_t maxFontSize = fontSize; while (i < lineLength) { uint32_t unicode = TypedText::GetUTF8Next(text, i, i); uint8_t ret = GetGlyphNode(unicode, glyphNode, fontId, fontSize); if (ret == RET_VALUE_OK) { uint8_t weight = GetFontWeight(glyphNode.fontId); // 16: bit rgb565 rgba8888 if (weight >= 16) { emojiMaxNode = glyphNode.rows > emojiMaxNode.rows ? glyphNode : emojiMaxNode; emojiNum++; } else { textNum++; } loopNum++; } } // The number of emoji is the same as the number of cycles, indicating that this line is all emoji // The number of words is the same as the number of cycles, which means that this line is all words if ((emojiNum == loopNum) || (textNum == loopNum)) { isEmojiLarge = true; return 0; } isEmojiLarge = emojiMaxNode.rows > maxFontSize; uint16_t offset = 0; if (isEmojiLarge) { // If the emoji is higher than the text if (emojiMaxNode.top >= maxFontSize) { offset = emojiMaxNode.top - maxFontSize; } } else { // If text are higher than emoji if (maxFontSize >= emojiMaxNode.rows) { offset = maxFontSize - emojiMaxNode.rows; } } return offset; } uint16_t UIFontVector::GetLineMaxHeight(const char* text, uint16_t lineLength, uint16_t fontId, uint8_t fontSize, uint16_t& letterIndex, SpannableString* spannableString) { if (!freeTypeInited_) { return INVALID_RET_VALUE; } uint32_t i = 0; uint16_t textNum = 0; uint16_t emojiNum = 0; uint16_t loopNum = 0; uint16_t maxHeight = GetHeight(fontId, fontSize); while (i < lineLength) { uint32_t unicode = TypedText::GetUTF8Next(text, i, i); TypedText::IsColourWord(unicode, fontId, fontSize) ? emojiNum++ : textNum++; loopNum++; if (spannableString != nullptr && spannableString->GetSpannable(letterIndex)) { int16_t spannableHeight = 0; spannableString->GetFontHeight(letterIndex, spannableHeight, fontId, fontSize); uint16_t tempHeight = static_cast(spannableHeight); maxHeight = tempHeight > maxHeight ? tempHeight : maxHeight; } letterIndex++; if (i > 0 && ((text[i - 1] == '\r') || (text[i - 1] == '\n'))) { break; } } return GetMaxSubLineHeight(textNum, loopNum, maxHeight, emojiNum); } uint16_t UIFontVector::GetMaxSubLineHeight(uint16_t textNum, uint16_t loopNum, uint16_t maxHeight, uint16_t emojiNum) { // The number of words is the same as the number of cycles, which means that this line is all words if (textNum == loopNum) { for (uint8_t i = 0; i < currentFontInfoNum_; i++) { if (!IsEmojiFont(i)) { uint16_t height = static_cast(ftFaces_[i]->size->metrics.height / FONT_PIXEL_IN_POINT); if (height > maxHeight) { maxHeight = height; } return maxHeight; } } } // The number of emoji is the same as the number of cycles, indicating that this line is all emoji if (emojiNum == loopNum) { for (uint8_t i = 0; i < currentFontInfoNum_; i++) { if (IsEmojiFont(i)) { return static_cast(ftFaces_[i]->size->metrics.height / FONT_PIXEL_IN_POINT); } } } // A line has both emoji and words if ((textNum > 0) && (emojiNum > 0)) { for (uint8_t i = 0; i < currentFontInfoNum_; i++) { uint16_t tmpHeight = static_cast(ftFaces_[i]->size->metrics.height / FONT_PIXEL_IN_POINT); maxHeight = tmpHeight > maxHeight ? tmpHeight : maxHeight; } } return maxHeight; } void UIFontVector::SetPsramMemory(uintptr_t psramAddr, uint32_t psramLen) { BaseFont::SetPsramMemory(psramAddr, psramLen); FontRamAllocator::GetInstance().SetRamAddr(psramAddr, psramLen); } int8_t UIFontVector::SetCurrentLangId(uint8_t langId) { GRAPHIC_LOGE("UIFontVector::SetCurrentLangId start"); FontRamAllocator::GetInstance().ClearRam(); #if defined(ENABLE_SHAPING) && ENABLE_SHAPING UITextShaping::GetInstance()->ClearTtfHeader(); #endif UIFontCacheManager* fontCacheManager = UIFontCacheManager::GetInstance(); fontCacheManager->ClearCacheFlag(); fontCacheManager->BitmapCacheClear(); if (fontCacheManager->GlyphsCacheInit() != RET_VALUE_OK) { GRAPHIC_LOGE("UIFontCacheManager::GlyphsCacheInit init failed"); return INVALID_RET_VALUE; } fontCacheManager->BitmapCacheInit(); return RET_VALUE_OK; } } // namespace OHOS