2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
22 #include <dali/devel-api/text-abstraction/font-list.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/platform-abstraction.h>
25 #include <dali/integration-api/trace.h>
26 #include <dali/internal/adaptor/common/adaptor-impl.h>
27 #include <dali/internal/imaging/common/image-operations.h>
28 #include <dali/internal/system/common/logging.h>
29 #include <dali/internal/text/text-abstraction/plugin/bitmap-font-cache-item.h>
30 #include <dali/internal/text/text-abstraction/plugin/embedded-item.h>
31 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.h>
32 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
33 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
34 #include <dali/public-api/common/dali-vector.h>
35 #include <dali/public-api/common/vector-wrapper.h>
38 #include <fontconfig/fontconfig.h>
42 // Use this macro only if need to log messages before the log function is set.
43 #define FONT_LOG_MESSAGE(level, format, ...) \
47 int result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
48 if(result >= static_cast<int>(sizeof(buffer))) \
50 std::string log("Font log message is too long to fit in the buffer.\n"); \
51 Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log); \
54 std::string log(buffer); \
55 Dali::TizenPlatform::LogMessage(level, log); \
58 #if defined(DEBUG_ENABLED)
60 // Note, to turn on trace and verbose logging, use "export LOG_FONT_CLIENT=3,true"
61 // Or re-define the following filter using Verbose,true instead of NoLogging,false,
62 // Or, add DALI_LOG_FILTER_ENABLE_TRACE(gFontClientLogFilter) in the code below.
64 Dali::Integration::Log::Filter* gFontClientLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
66 #define FONT_LOG_DESCRIPTION(fontDescription, prefix) \
67 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix " description; family : [%s]\n", fontDescription.family.c_str()); \
68 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, \
72 " slant : [%s]\n\n", \
73 fontDescription.path.c_str(), \
74 FontWidth::Name[fontDescription.width], \
75 FontWeight::Name[fontDescription.weight], \
76 FontSlant::Name[fontDescription.slant])
78 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
79 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, \
81 " requestedPointSize : %d\n" \
82 " preferColor : %s\n", \
85 (preferColor ? "true" : "false"))
89 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
90 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
96 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_FONT_PERFORMANCE_MARKER, false);
99 * Conversion from Fractional26.6 to float
101 const float FROM_266 = 1.0f / 64.0f;
102 const float POINTS_PER_INCH = 72.f;
104 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
110 namespace Dali::TextAbstraction::Internal
115 * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
117 * @param[in/out] ftFace Face type object.
118 * @param[in] horizontalDpi The horizontal dpi.
119 * @param[in] verticalDpi The vertical dpi.
120 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
121 * @param[in] requestedPointSize The requested point-size.
122 * @return whether the ftFace's block can fit into atlas
124 bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
128 error = FT_Set_Char_Size(ftFace,
130 FT_F26Dot6(requestedPointSize),
134 if(error == FT_Err_Ok)
136 //Check width and height of block for requestedPointSize
137 //If the width or height is greater than the maximum-size then decrement by one unit of point-size.
138 if(static_cast<float>(ftFace->size->metrics.height) * FROM_266 <= maxSizeFitInAtlas.height && (static_cast<float>(ftFace->size->metrics.ascender) - static_cast<float>(ftFace->size->metrics.descender)) * FROM_266 <= maxSizeFitInAtlas.width)
148 * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
150 * @param[in/out] ftFace Face type object.
151 * @param[in] horizontalDpi The horizontal dpi.
152 * @param[in] verticalDpi The vertical dpi.
153 * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
154 * @param[in/out] requestedPointSize The requested point-size.
155 * @return FreeType error code. 0 means success when requesting the nominal size (in points).
157 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
159 //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
160 const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
162 int error; // FreeType error code.
164 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
165 if(FT_Err_Ok != error)
173 uint32_t exponentialDecrement = 1;
175 while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit * exponentialDecrement)
177 requestedPointSize -= (pointSizePerOneUnit * exponentialDecrement);
178 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
179 if(FT_Err_Ok != error)
184 exponentialDecrement *= 2;
188 uint32_t minPointSize;
189 uint32_t maxPointSize;
193 exponentialDecrement /= 2;
194 minPointSize = requestedPointSize;
195 maxPointSize = requestedPointSize + (pointSizePerOneUnit * exponentialDecrement);
200 maxPointSize = requestedPointSize;
203 while(minPointSize < maxPointSize)
205 requestedPointSize = ((maxPointSize / pointSizePerOneUnit - minPointSize / pointSizePerOneUnit) / 2) * pointSizePerOneUnit + minPointSize;
206 canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
207 if(FT_Err_Ok != error)
214 if(minPointSize == requestedPointSize)
216 //Found targeted point-size
220 minPointSize = requestedPointSize;
224 maxPointSize = requestedPointSize;
234 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
235 unsigned int verticalDpi)
236 : mFreeTypeLibrary(nullptr),
237 mDpiHorizontal(horizontalDpi),
238 mDpiVertical(verticalDpi),
239 mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
240 mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS),
241 mVectorFontCache(nullptr),
242 mCacheHandler(new CacheHandler())
244 int error = FT_Init_FreeType(&mFreeTypeLibrary);
245 if(FT_Err_Ok != error)
247 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Init error: %d\n", error);
250 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
251 mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
255 FontClient::Plugin::~Plugin()
257 // Delete cache hanlder before remove mFreeTypeLibrary
258 delete mCacheHandler;
260 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
261 delete mVectorFontCache;
264 FT_Done_FreeType(mFreeTypeLibrary);
267 void FontClient::Plugin::ClearCache() const
269 mCacheHandler->ClearCache();
272 void FontClient::Plugin::ClearCacheOnLocaleChanged() const
274 mCacheHandler->ClearCacheOnLocaleChanged();
277 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
278 unsigned int verticalDpi)
280 mDpiHorizontal = horizontalDpi;
281 mDpiVertical = verticalDpi;
284 void FontClient::Plugin::ResetSystemDefaults() const
286 mCacheHandler->ResetSystemDefaults();
289 void FontClient::Plugin::CacheFontDataFromFile(const std::string& fontPath) const
296 if(mCacheHandler->FindFontData(fontPath))
298 // Font data is already cached, no need to reload
302 Dali::Vector<uint8_t> fontDataBuffer;
303 std::streampos dataSize = 0;
304 if(!mCacheHandler->LoadFontDataFromFile(fontPath, fontDataBuffer, dataSize))
306 fontDataBuffer.Clear();
307 FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "Failed to load font data : %s\n", fontPath.c_str());
312 mCacheHandler->CacheFontData(fontPath, fontDataBuffer, dataSize);
315 void FontClient::Plugin::CacheFontFaceFromFile(const std::string& fontPath) const
322 if(mCacheHandler->FindFontFace(fontPath))
324 // Font face is already cached, no need to reload
329 int error = FT_New_Face(mFreeTypeLibrary, fontPath.c_str(), 0, &ftFace);
330 if(FT_Err_Ok != error)
332 FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "Failed to load font face : %s\n", fontPath.c_str());
337 mCacheHandler->CacheFontFace(fontPath, ftFace);
338 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "PreLoad font new face : %s\n", fontPath.c_str());
341 void FontClient::Plugin::FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList) const
343 for(const auto& fontPath : fontPathList)
345 CacheFontFaceFromFile(fontPath);
348 for(const auto& memoryFontPath : memoryFontPathList)
350 CacheFontDataFromFile(memoryFontPath);
354 void FontClient::Plugin::FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily) const
356 mCacheHandler->InitDefaultFontDescription();
358 FontFamilyList familyList;
359 familyList.reserve(extraFamilyList.size() + 1);
361 for(const auto& fallbackFont : fallbackFamilyList)
363 FontList* fontList = nullptr;
364 CharacterSetList* characterSetList = nullptr;
365 FontDescriptionId fontDescriptionId = 0u;
366 FontDescription fontDescription;
367 fontDescription.family = FontFamily(fallbackFont);
368 fontDescription.weight = DefaultFontWeight();
369 fontDescription.width = DefaultFontWidth();
370 fontDescription.slant = DefaultFontSlant();
372 if(!mCacheHandler->FindFallbackFontList(fontDescription, fontList, characterSetList))
374 FontDescription copiedFontDescription = fontDescription;
375 mCacheHandler->CacheFallbackFontList(std::move(copiedFontDescription), fontList, characterSetList);
377 if(!mCacheHandler->FindValidatedFont(fontDescription, fontDescriptionId))
379 mCacheHandler->ValidateFont(fontDescription, fontDescriptionId);
382 if(extraFamilyList.empty() && localeFamily.empty())
388 familyList.insert(familyList.end(), extraFamilyList.begin(), extraFamilyList.end());
389 if(!localeFamily.empty())
391 familyList.push_back(localeFamily);
394 for(const auto& font : *fontList)
396 auto it = std::find(familyList.begin(), familyList.end(), font.family);
397 if(it != familyList.end())
399 if(!mCacheHandler->FindValidatedFont(font, fontDescriptionId))
401 mCacheHandler->ValidateFont(font, fontDescriptionId);
403 familyList.erase(it);
409 void FontClient::Plugin::InitDefaultFontDescription() const
411 mCacheHandler->InitDefaultFontDescription();
414 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription) const
416 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
418 mCacheHandler->InitDefaultFontDescription();
419 fontDescription = mCacheHandler->mDefaultFontDescription;
421 FONT_LOG_DESCRIPTION(fontDescription, "");
424 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts) const
426 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
428 mCacheHandler->InitDefaultFonts();
429 defaultFonts = mCacheHandler->mDefaultFonts;
431 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of default fonts : [%d]\n", mCacheHandler->mDefaultFonts.size());
434 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts) const
436 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
438 mCacheHandler->InitSystemFonts();
439 systemFonts = mCacheHandler->mSystemFonts;
441 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of system fonts : [%d]\n", mCacheHandler->mSystemFonts.size());
444 void FontClient::Plugin::GetDescription(FontId fontId,
445 FontDescription& fontDescription) const
447 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
448 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
450 if((fontId > 0u) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
452 const auto& fontIdCacheItem = mCacheHandler->mFontIdCache[fontId - 1u];
453 switch(fontIdCacheItem.type)
455 case FontDescription::FACE_FONT:
457 for(const auto& item : mCacheHandler->mFontDescriptionSizeCache)
459 if(item.second == fontIdCacheItem.index)
461 fontDescription = *(mCacheHandler->mFontDescriptionCache.begin() + item.first.fontDescriptionId - 1u);
463 FONT_LOG_DESCRIPTION(fontDescription, "");
469 case FontDescription::BITMAP_FONT:
471 fontDescription.type = FontDescription::BITMAP_FONT;
472 fontDescription.family = mCacheHandler->mBitmapFontCache[fontIdCacheItem.index].font.name;
477 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
478 fontDescription.type = FontDescription::INVALID;
479 fontDescription.family.clear();
484 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " No description found for the font id %d\n", fontId);
487 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId fontId) const
489 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
490 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
492 PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
493 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
494 if(fontCacheItem != nullptr)
496 pointSize = fontCacheItem->GetPointSize();
498 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " point size : %d\n", pointSize);
503 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character) const
505 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
506 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
507 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " character : %p\n", character);
509 bool isSupported = false;
510 auto fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
511 if(fontCacheItem != nullptr)
513 isSupported = fontCacheItem->IsCharacterSupported(character); // May cache
516 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false"));
520 const FontCacheItemInterface* FontClient::Plugin::GetCachedFontItem(FontId fontId) const
522 if((fontId > 0u) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
524 const auto& fontIdCacheItem = mCacheHandler->mFontIdCache[fontId - 1u];
525 switch(fontIdCacheItem.type)
527 case FontDescription::FACE_FONT:
529 return &mCacheHandler->mFontFaceCache[fontIdCacheItem.index];
531 case FontDescription::BITMAP_FONT:
533 return &mCacheHandler->mBitmapFontCache[fontIdCacheItem.index];
537 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Invalid type of font\n");
544 FontId FontClient::Plugin::FindFontForCharacter(const FontList& fontList,
545 const CharacterSetList& characterSetList,
547 PointSize26Dot6 requestedPointSize,
548 bool preferColor) const
550 DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
551 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
552 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " character : %p\n", character);
553 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
554 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false"));
557 bool foundColor = false;
559 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of fonts : %d\n", fontList.size());
561 // Traverse the list of fonts.
562 // Check for each font if supports the character.
563 for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
565 const FontDescription& description = fontList[index];
566 const FcCharSet* const characterSet = characterSetList[index];
568 FONT_LOG_DESCRIPTION(description, "");
570 bool foundInRanges = false;
571 if(nullptr != characterSet)
573 foundInRanges = FcCharSetHasChar(characterSet, character);
578 fontId = GetFontId(description, requestedPointSize, 0u);
580 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " font id : %d\n", fontId);
584 if((fontId > 0) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
586 const FontFaceCacheItem& item = mCacheHandler->mFontFaceCache[mCacheHandler->mFontIdCache[fontId - 1u].index];
588 foundColor = item.mHasColorTables;
591 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " foundColor : %s\n", (foundColor ? "true" : "false"));
594 // Keep going unless we prefer a different (color) font.
595 if(!preferColor || foundColor)
602 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
606 FontId FontClient::Plugin::FindDefaultFont(Character charcode,
607 PointSize26Dot6 requestedPointSize,
608 bool preferColor) const
610 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
611 FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
615 // Create the list of default fonts if it has not been created.
616 mCacheHandler->InitDefaultFonts();
617 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of default fonts : %d\n", mCacheHandler->mDefaultFonts.size());
619 // Traverse the list of default fonts.
620 // Check for each default font if supports the character.
621 fontId = FindFontForCharacter(mCacheHandler->mDefaultFonts, mCacheHandler->mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
623 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
627 FontId FontClient::Plugin::FindFallbackFont(Character charcode,
628 const FontDescription& preferredFontDescription,
629 PointSize26Dot6 requestedPointSize,
630 bool preferColor) const
632 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
633 FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
635 DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_FIND_FALLBACKFONT");
637 // The font id to be returned.
640 FontDescription fontDescription;
642 // Fill the font description with the preferred font description and complete with the defaults.
643 fontDescription.family = preferredFontDescription.family.empty() ? DefaultFontFamily() : preferredFontDescription.family;
644 fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? DefaultFontWeight() : preferredFontDescription.weight);
645 fontDescription.width = ((FontWidth::NONE == preferredFontDescription.width) ? DefaultFontWidth() : preferredFontDescription.width);
646 fontDescription.slant = ((FontSlant::NONE == preferredFontDescription.slant) ? DefaultFontSlant() : preferredFontDescription.slant);
648 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n");
649 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
650 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
651 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
652 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
654 #if defined(TRACE_ENABLED)
655 if(gTraceFilter && gTraceFilter->IsTraceEnabled())
657 DALI_LOG_DEBUG_INFO("DALI_TEXT_FIND_FALLBACKFONT : %s -> %s\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
661 // Check first if the font's description has been queried before.
662 FontList* fontList = nullptr;
663 CharacterSetList* characterSetList = nullptr;
665 if(!mCacheHandler->FindFallbackFontList(fontDescription, fontList, characterSetList))
667 mCacheHandler->CacheFallbackFontList(std::move(fontDescription), fontList, characterSetList);
670 if(fontList && characterSetList)
672 fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
675 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
679 FontId FontClient::Plugin::GetFontIdByPath(const FontPath& path,
680 PointSize26Dot6 requestedPointSize,
682 bool cacheDescription) const
684 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
685 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
686 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
690 if(nullptr != mFreeTypeLibrary)
693 if(mCacheHandler->FindFontByPath(path, requestedPointSize, faceIndex, foundId))
699 id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
703 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", id);
707 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
708 PointSize26Dot6 requestedPointSize,
709 FaceIndex faceIndex) const
711 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
712 FONT_LOG_DESCRIPTION(fontDescription, "");
714 // Special case when font Description don't have family information.
715 // In this case, we just use default description family and path.
716 const FontDescription& realFontDescription = fontDescription.family.empty() ? FontDescription(mCacheHandler->mDefaultFontDescription.path,
717 mCacheHandler->mDefaultFontDescription.family,
718 fontDescription.width,
719 fontDescription.weight,
720 fontDescription.slant,
721 fontDescription.type)
724 FONT_LOG_DESCRIPTION(realFontDescription, "");
725 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
727 // This method uses three vectors which caches:
728 // * The bitmap font cache
729 // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
730 // * The path to font file names.
731 // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
733 // 1) Checks if the font description matches with a previously loaded bitmap font.
734 // Returns if a font is found.
735 // 2) Checks in the cache if the font's description has been validated before.
736 // If it was it gets an index to the vector with paths to font file names. Otherwise,
737 // retrieves using font config a path to a font file name which matches with the
738 // font's description. The path is stored in the cache.
740 // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
741 // font file names' exists. If exists, it gets the font id. If it doesn't it calls
742 // the GetFontId() method with the path to the font file name and the point size to
745 // The font id to be returned.
748 // Check first if the font description matches with a previously loaded bitmap font.
749 if(mCacheHandler->FindBitmapFont(realFontDescription.family, fontId))
754 // Check if the font's description have been validated before.
755 FontDescriptionId fontDescriptionId = 0u;
757 if(!mCacheHandler->FindValidatedFont(realFontDescription, fontDescriptionId))
759 // Use font config to validate the font's description.
760 mCacheHandler->ValidateFont(realFontDescription, fontDescriptionId);
763 using FontCacheIndex = CacheHandler::FontCacheIndex;
764 FontCacheIndex fontCacheIndex = 0u;
765 // Check if exists a pair 'fontDescriptionId, requestedPointSize' in the cache.
766 if(!mCacheHandler->FindFont(fontDescriptionId, requestedPointSize, fontCacheIndex))
768 if(fontDescriptionId > 0u && fontDescriptionId <= mCacheHandler->mCharacterSetCache.Count())
770 // Retrieve the font file name path.
771 const FontDescription& description = *(mCacheHandler->mFontDescriptionCache.begin() + fontDescriptionId - 1u);
773 // Retrieve the font id. Do not cache the description as it has been already cached.
774 // Note : CacheFontPath() API call ValidateFont() + setup CharacterSet + cache the font description.
775 // So set cacheDescription=false, that we don't call CacheFontPath().
776 fontId = GetFontIdByPath(description.path, requestedPointSize, faceIndex, false);
778 if((fontId > 0u) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
780 fontCacheIndex = mCacheHandler->mFontIdCache[fontId - 1u].index;
781 mCacheHandler->mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCacheHandler->mCharacterSetCache[fontDescriptionId - 1u]);
783 // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
784 mCacheHandler->CacheFontDescriptionSize(fontDescriptionId, requestedPointSize, fontCacheIndex);
790 fontId = mCacheHandler->mFontFaceCache[fontCacheIndex].mFontId + 1u;
793 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
797 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont) const
799 // The font id to be returned.
801 if(!mCacheHandler->FindBitmapFont(bitmapFont.name, fontId))
803 BitmapFontCacheItem bitmapFontCacheItem(bitmapFont);
805 fontId = mCacheHandler->CacheBitmapFontCacheItem(std::move(bitmapFontCacheItem));
810 void FontClient::Plugin::GetFontMetrics(FontId fontId,
811 FontMetrics& metrics) const
813 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
814 if(fontCacheItem != nullptr)
816 fontCacheItem->GetFontMetrics(metrics, mDpiVertical);
820 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId fontId,
821 Character charcode) const
823 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
824 if(fontCacheItem != nullptr)
826 return fontCacheItem->GetGlyphIndex(charcode);
832 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId fontId,
834 Character variantSelector) const
836 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
837 if(fontCacheItem != nullptr)
839 return fontCacheItem->GetGlyphIndex(charcode, variantSelector);
845 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
848 bool horizontal) const
850 if(VECTOR_GLYPH == type)
852 return GetVectorMetrics(array, size, horizontal);
855 return GetBitmapMetrics(array, size, horizontal);
858 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
860 bool horizontal) const
862 bool success(size > 0u);
864 for(unsigned int i = 0; i < size; ++i)
866 GlyphInfo& glyph = array[i];
868 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(glyph.fontId);
869 if(fontCacheItem != nullptr)
871 success &= fontCacheItem->GetGlyphMetrics(glyph, mDpiVertical, horizontal);
873 // Check if it's an embedded image.
874 else if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mCacheHandler->mEmbeddedItemCache.size()))
876 mCacheHandler->mEmbeddedItemCache[glyph.index - 1u].GetGlyphMetrics(glyph);
887 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
889 bool horizontal) const
891 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
894 for(unsigned int i = 0u; i < size; ++i)
896 FontId fontId = array[i].fontId;
899 (fontId - 1u) < mCacheHandler->mFontIdCache.size())
901 FontFaceCacheItem& font = mCacheHandler->mFontFaceCache[mCacheHandler->mFontIdCache[fontId - 1u].index];
903 if(!font.mVectorFontId)
905 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
908 mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
910 // Vector metrics are in EMs, convert to pixels
911 const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
912 array[i].width *= scale;
913 array[i].height *= scale;
914 array[i].xBearing *= scale;
915 array[i].yBearing *= scale;
916 array[i].advance *= scale;
930 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::GlyphBufferData& data, int outlineWidth) const
932 data.isColorBitmap = false;
933 data.isColorEmoji = false;
934 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
935 if(fontCacheItem != nullptr)
937 fontCacheItem->CreateBitmap(glyphIndex, data, outlineWidth, isItalicRequired, isBoldRequired);
939 else if((0u != glyphIndex) && (glyphIndex <= mCacheHandler->mEmbeddedItemCache.size()))
941 // It's an embedded item.
942 mCacheHandler->mEmbeddedItemCache[glyphIndex - 1u].CreateBitmap(mCacheHandler->mPixelBufferCache, data);
946 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth) const
948 TextAbstraction::GlyphBufferData data;
950 CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
952 // If data is compressed or not owned buffer, copy this.
953 if(!data.isBufferOwned || data.compressionType != TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION)
955 uint8_t* newBuffer = (uint8_t*)malloc(data.width * data.height * Pixel::GetBytesPerPixel(data.format));
956 if(DALI_UNLIKELY(!newBuffer))
958 DALI_LOG_ERROR("malloc is failed. request malloc size : %u x %u x %u\n", data.width, data.height, Pixel::GetBytesPerPixel(data.format));
959 return Dali::PixelData();
962 TextAbstraction::GlyphBufferData::Decompress(data, newBuffer);
963 if(data.isBufferOwned)
968 data.buffer = newBuffer;
969 data.isBufferOwned = true;
970 data.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
973 return PixelData::New(data.buffer,
974 data.width * data.height * Pixel::GetBytesPerPixel(data.format),
981 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight) const
986 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
988 (fontId - 1u < mCacheHandler->mFontIdCache.size()))
990 using FontCacheIndex = CacheHandler::FontCacheIndex;
991 const FontCacheIndex fontCacheIndex = mCacheHandler->mFontIdCache[fontId - 1u].index;
992 FontFaceCacheItem& font = mCacheHandler->mFontFaceCache[fontCacheIndex];
994 if(!font.mVectorFontId)
996 font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
999 mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontCacheIndex, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1004 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize) const
1006 using EllipsisCacheIndex = CacheHandler::EllipsisCacheIndex;
1007 using EllipsisItem = CacheHandler::EllipsisItem;
1008 EllipsisCacheIndex ellipsisCacheIndex = 0u;
1010 if(!mCacheHandler->FindEllipsis(requestedPointSize, ellipsisCacheIndex))
1012 // No glyph has been found. Create one.
1015 item.requestedPointSize = requestedPointSize;
1017 // Find a font for the ellipsis glyph.
1018 item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1022 // Set the character index to access the glyph inside the font.
1023 item.glyph.index = GetGlyphIndex(item.glyph.fontId, ELLIPSIS_CHARACTER);
1025 // Get glyph informations.
1026 GetBitmapMetrics(&item.glyph, 1u, true);
1028 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
1029 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
1031 // EllipsisCacheIndex is stored in item.index.
1032 ellipsisCacheIndex = mCacheHandler->CacheEllipsis(std::move(item));
1033 if(!mCacheHandler->mEllipsisCache.empty())
1035 mCacheHandler->mEllipsisCache.back().index = ellipsisCacheIndex;
1038 return mCacheHandler->mEllipsisCache[ellipsisCacheIndex].glyph;
1041 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex) const
1043 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1044 return fontCacheItem && fontCacheItem->IsColorGlyph(glyphIndex);
1047 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId) const
1049 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1050 if(fontCacheItem != nullptr)
1052 return fontCacheItem->GetTypeface();
1057 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId) const
1059 const FontId index = fontId - 1u;
1060 if((fontId > 0u) && (index < mCacheHandler->mFontIdCache.size()))
1062 return mCacheHandler->mFontIdCache[index].type;
1064 return FontDescription::INVALID;
1067 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1069 // nullptr as first parameter means the current configuration is used.
1070 return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1073 HarfBuzzFontHandle FontClient::Plugin::GetHarfBuzzFont(FontId fontId) const
1075 FontCacheItemInterface* fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
1076 if(fontCacheItem != nullptr)
1078 return fontCacheItem->GetHarfBuzzFont(mDpiHorizontal, mDpiVertical); // May cache
1083 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat) const
1085 EmbeddedItem embeddedItem;
1087 embeddedItem.pixelBufferId = 0u;
1088 embeddedItem.width = description.width;
1089 embeddedItem.height = description.height;
1091 pixelFormat = Pixel::A8;
1093 if(!description.url.empty())
1095 // Check if the url is in the cache.
1096 Devel::PixelBuffer pixelBuffer;
1097 if(!mCacheHandler->FindEmbeddedPixelBufferId(description.url, embeddedItem.pixelBufferId))
1099 // The pixel buffer is not in the cache. Create one and cache it.
1100 embeddedItem.pixelBufferId = mCacheHandler->CacheEmbeddedPixelBuffer(description.url);
1103 if((embeddedItem.pixelBufferId > 0u) && (embeddedItem.pixelBufferId - 1u) < mCacheHandler->mPixelBufferCache.size())
1105 // Retrieve the pixel buffer from the cache to set the pixel format.
1106 pixelBuffer = mCacheHandler->mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1111 // Set the size of the embedded item if it has not been set.
1112 if(0u == embeddedItem.width)
1114 embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1117 if(0u == embeddedItem.height)
1119 embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1122 // Set the pixel format.
1123 pixelFormat = pixelBuffer.GetPixelFormat();
1127 // Find if the same embeddedItem has already been created.
1128 GlyphIndex index = 0u;
1129 if(!mCacheHandler->FindEmbeddedItem(embeddedItem.pixelBufferId, embeddedItem.width, embeddedItem.height, index))
1131 index = mCacheHandler->CacheEmbeddedItem(std::move(embeddedItem));
1136 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
1138 mIsAtlasLimitationEnabled = enabled;
1141 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
1143 return mIsAtlasLimitationEnabled;
1146 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
1148 return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1151 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
1153 return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
1156 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
1158 return mCurrentMaximumBlockSizeFitInAtlas;
1161 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
1163 bool isChanged = false;
1164 const Size& maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1165 const uint16_t& padding = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
1167 if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
1169 mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
1176 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
1178 return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
1181 FontId FontClient::Plugin::CreateFont(const FontPath& path,
1182 PointSize26Dot6 requestedPointSize,
1183 FaceIndex faceIndex,
1184 bool cacheDescription) const
1186 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1187 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
1188 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
1190 DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_CREATE_FONT");
1196 uint8_t* fontDataPtr = nullptr;
1197 std::streampos dataSize = 0;
1198 bool fontDataFound = mCacheHandler->FindFontData(path, fontDataPtr, dataSize);
1202 // Create & cache new font face from pre-loaded font
1203 error = FT_New_Memory_Face(mFreeTypeLibrary, reinterpret_cast<FT_Byte*>(fontDataPtr), static_cast<FT_Long>(dataSize), 0, &ftFace);
1204 #if defined(TRACE_ENABLED)
1205 if(gTraceFilter && gTraceFilter->IsTraceEnabled())
1207 DALI_LOG_DEBUG_INFO("DALI_TEXT_CREATE_FONT : FT_New_Memory_Face : %s\n", path.c_str());
1213 // Create & cache new font face
1214 error = FT_New_Face(mFreeTypeLibrary, path.c_str(), 0, &ftFace);
1215 #if defined(TRACE_ENABLED)
1216 if(gTraceFilter && gTraceFilter->IsTraceEnabled())
1218 DALI_LOG_DEBUG_INFO("DALI_TEXT_CREATE_FONT : FT_New_Face : %s\n", path.c_str());
1223 if(FT_Err_Ok == error)
1225 // Check if a font is scalable.
1226 const bool isScalable = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
1227 const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
1228 const bool hasColorTables = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
1230 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " isScalable : [%s]\n", (isScalable ? "true" : "false"));
1231 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
1232 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
1234 // Check to see if the font contains fixed sizes?
1235 if(!isScalable && hasFixedSizedBitmaps)
1237 PointSize26Dot6 actualPointSize = 0u;
1238 int fixedSizeIndex = 0;
1239 for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
1241 const PointSize26Dot6 fixedSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
1242 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
1244 if(fixedSize >= requestedPointSize)
1246 actualPointSize = fixedSize;
1251 if(0u == actualPointSize)
1253 // The requested point size is bigger than the bigest fixed size.
1254 fixedSizeIndex = ftFace->num_fixed_sizes - 1;
1255 actualPointSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
1258 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
1260 // Tell Freetype to use this size
1261 error = FT_Select_Size(ftFace, fixedSizeIndex);
1262 if(FT_Err_Ok != error)
1264 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
1268 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1270 FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1271 static_cast<float>(ftMetrics.descender) * FROM_266,
1272 static_cast<float>(ftMetrics.height) * FROM_266,
1273 static_cast<float>(ftFace->underline_position) * FROM_266,
1274 static_cast<float>(ftFace->underline_thickness) * FROM_266);
1276 const float fixedWidth = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
1277 const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
1279 // Create the FreeType font face item to cache.
1280 FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
1282 fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
1287 if(mIsAtlasLimitationEnabled)
1289 //There is limitation on block size to fit in predefined atlas size.
1290 //If the block size cannot fit into atlas size, then the system cannot draw block.
1291 //This is workaround to avoid issue in advance
1292 //Decrementing point-size until arriving to maximum allowed block size.
1293 auto requestedPointSizeBackup = requestedPointSize;
1294 const Size& maxSizeFitInAtlas = GetCurrentMaximumBlockSizeFitInAtlas();
1295 error = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
1297 if(requestedPointSize != requestedPointSizeBackup)
1299 DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
1304 error = FT_Set_Char_Size(ftFace,
1306 FT_F26Dot6(requestedPointSize),
1311 if(FT_Err_Ok == error)
1313 FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1315 FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1316 static_cast<float>(ftMetrics.descender) * FROM_266,
1317 static_cast<float>(ftMetrics.height) * FROM_266,
1318 static_cast<float>(ftFace->underline_position) * FROM_266,
1319 static_cast<float>(ftFace->underline_thickness) * FROM_266);
1321 // Create the FreeType font face item to cache.
1322 FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics);
1324 fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
1328 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
1334 if(cacheDescription)
1336 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " Cache Font Path at font id : %d [%s]\n", fontId, path.c_str());
1337 mCacheHandler->CacheFontPath(ftFace, fontId, requestedPointSize, path);
1343 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str());
1346 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font id : %d\n", fontId);
1350 bool FontClient::Plugin::IsScalable(const FontPath& path) const
1352 bool isScalable = false;
1354 FT_Face ftFace = nullptr;
1355 int error = FT_New_Face(mFreeTypeLibrary,
1359 if(FT_Err_Ok != error)
1361 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
1365 isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
1370 FT_Done_Face(ftFace);
1376 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription) const
1378 // Create a font pattern.
1379 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1381 FcResult result = FcResultMatch;
1383 // match the pattern
1384 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1385 bool isScalable = false;
1389 // Get the path to the font file name.
1391 GetFcString(match, FC_FILE, path);
1392 isScalable = IsScalable(path);
1396 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1399 // Destroys the created patterns.
1400 FcPatternDestroy(match);
1401 FcPatternDestroy(fontFamilyPattern);
1406 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes) const
1408 // Empty the caller container
1411 FT_Face ftFace = nullptr;
1412 int error = FT_New_Face(mFreeTypeLibrary,
1416 if(FT_Err_Ok != error)
1418 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
1423 // Fetch the number of fixed sizes available
1424 if(ftFace->num_fixed_sizes && ftFace->available_sizes)
1426 for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
1428 sizes.PushBack(ftFace->available_sizes[i].size);
1432 FT_Done_Face(ftFace);
1436 void FontClient::Plugin::GetFixedSizes(const FontDescription& fontDescription,
1437 Vector<PointSize26Dot6>& sizes) const
1439 // Create a font pattern.
1440 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1442 FcResult result = FcResultMatch;
1444 // match the pattern
1445 FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1449 // Get the path to the font file name.
1451 GetFcString(match, FC_FILE, path);
1452 GetFixedSizes(path, sizes);
1456 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1459 // Destroys the created patterns.
1460 FcPatternDestroy(match);
1461 FcPatternDestroy(fontFamilyPattern);
1464 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
1466 const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1467 if(fontCacheItem != nullptr)
1469 return fontCacheItem->HasItalicStyle();
1474 } // namespace Dali::TextAbstraction::Internal