2 * Copyright (c) 2023 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-cache-handler.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/trace.h>
24 #include <fontconfig/fontconfig.h>
27 #include <dali/devel-api/adaptor-framework/environment-variable.h>
28 #include <dali/devel-api/adaptor-framework/file-loader.h>
29 #include <dali/devel-api/adaptor-framework/image-loading.h>
30 #include <dali/internal/system/common/logging.h>
31 #include <dali/internal/text/text-abstraction/font-client-impl.h>
32 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
33 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
35 // Use this macro only if need to log messages before the log function is set.
36 #define FONT_LOG_MESSAGE(level, format, ...) \
40 int result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
41 if(result >= static_cast<int>(sizeof(buffer))) \
43 std::string log("Font log message is too long to fit in the buffer.\n"); \
44 Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log); \
47 std::string log(buffer); \
48 Dali::TizenPlatform::LogMessage(level, log); \
51 #if defined(DEBUG_ENABLED)
52 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
54 #define FONT_LOG_DESCRIPTION(fontDescription, prefix) \
55 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix " description; family : [%s]\n", fontDescription.family.c_str()); \
56 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, \
60 " slant : [%s]\n\n", \
61 fontDescription.path.c_str(), \
62 FontWidth::Name[fontDescription.width], \
63 FontWeight::Name[fontDescription.weight], \
64 FontSlant::Name[fontDescription.slant])
66 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
67 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, \
69 " requestedPointSize : %d\n" \
70 " preferColor : %s\n", \
73 (preferColor ? "true" : "false"))
77 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
78 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
85 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_FONT_PERFORMANCE_MARKER, false);
88 * @brief Maximum size of glyph cache per each font face.
90 constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX = 128;
91 constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u;
93 constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
96 * @brief Get maximum size of glyph cache size from environment.
97 * If not settuped, default as 128.
98 * @note This value fixed when we call it first time.
99 * @return The max size of glyph cache.
101 inline size_t GetMaxNumberOfGlyphCache()
103 using Dali::EnvironmentVariable::GetEnvironmentVariable;
104 static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV);
105 static auto number = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX;
106 return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number;
111 namespace Dali::TextAbstraction::Internal
116 * @brief Free the resources allocated by the FcCharSet objects.
118 * @param[in] characterSets The vector of character sets.
120 void DestroyCharacterSets(CharacterSetList& characterSets)
122 for(auto& item : characterSets)
126 FcCharSetDestroy(item);
132 * @brief Retrieves the fonts present in the platform.
134 * @note Need to call FcFontSetDestroy to free the allocated resources.
136 * @return A font fonfig data structure with the platform's fonts.
138 _FcFontSet* GetFcFontSet()
140 FcFontSet* fontset = nullptr;
142 // create a new pattern.
143 // a pattern holds a set of names, each name refers to a property of the font
144 FcPattern* pattern = FcPatternCreate();
146 if(nullptr != pattern)
148 // create an object set used to define which properties are to be returned in the patterns from FcFontList.
149 FcObjectSet* objectSet = FcObjectSetCreate();
151 if(nullptr != objectSet)
153 // build an object set from a list of property names
154 FcObjectSetAdd(objectSet, FC_FILE);
155 FcObjectSetAdd(objectSet, FC_FAMILY);
156 FcObjectSetAdd(objectSet, FC_WIDTH);
157 FcObjectSetAdd(objectSet, FC_WEIGHT);
158 FcObjectSetAdd(objectSet, FC_SLANT);
160 // get a list of fonts
161 // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
162 fontset = FcFontList(nullptr /* the default configuration is checked to be up to date, and used */, pattern, objectSet); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
164 // clear up the object set
165 FcObjectSetDestroy(objectSet);
168 // clear up the pattern
169 FcPatternDestroy(pattern);
176 * @brief Helper for GetDefaultFonts etc.
178 * @note CharacterSetList is a vector of FcCharSet that are reference counted. It's needed to call FcCharSetDestroy to decrease the reference counter.
180 * @param[in] fontDescription A font description.
181 * @param[out] fontList A list of the fonts which are a close match for fontDescription.
182 * @param[out] characterSetList A list of character sets which are a close match for fontDescription.
184 void SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
186 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
189 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
191 FcResult result = FcResultMatch;
193 // Match the pattern.
194 FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
196 false /* don't trim */,
198 &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
200 if(nullptr != fontSet)
202 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont);
203 // Reserve some space to avoid reallocations.
204 fontList.reserve(fontSet->nfont);
206 for(int i = 0u; i < fontSet->nfont; ++i)
208 FcPattern* fontPattern = fontSet->fonts[i];
212 // Skip fonts with no path
213 if(GetFcString(fontPattern, FC_FILE, path))
215 // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
216 FcCharSet* characterSet = nullptr;
217 FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
219 // Increase the reference counter of the character set.
220 characterSetList.PushBack(FcCharSetCopy(characterSet));
222 fontList.push_back(FontDescription());
223 FontDescription& newFontDescription = fontList.back();
225 newFontDescription.path = std::move(path);
230 GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
231 GetFcInt(fontPattern, FC_WIDTH, width);
232 GetFcInt(fontPattern, FC_WEIGHT, weight);
233 GetFcInt(fontPattern, FC_SLANT, slant);
234 newFontDescription.width = IntToWidthType(width);
235 newFontDescription.weight = IntToWeightType(weight);
236 newFontDescription.slant = IntToSlantType(slant);
238 FONT_LOG_DESCRIPTION(newFontDescription, "new font");
242 // Destroys the font set created by FcFontSort.
243 FcFontSetDestroy(fontSet);
247 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " No fonts found.\n");
250 // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
251 FcPatternDestroy(fontFamilyPattern);
256 FontClient::Plugin::CacheHandler::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
257 : fontDescription{std::move(font)},
258 fallbackFonts{fallbackFonts},
259 characterSets{characterSets}
263 FontClient::Plugin::CacheHandler::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
264 FontDescriptionId index)
265 : fontDescription{fontDescription},
270 FontClient::Plugin::CacheHandler::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
271 FontDescriptionId index)
272 : fontDescription{std::move(fontDescription)},
277 FontClient::Plugin::CacheHandler::FontDescriptionSizeCacheKey::FontDescriptionSizeCacheKey(FontDescriptionId fontDescriptionId,
278 PointSize26Dot6 requestedPointSize)
279 : fontDescriptionId(fontDescriptionId),
280 requestedPointSize(requestedPointSize)
285 FontClient::Plugin::CacheHandler::CacheHandler()
286 : mDefaultFontDescription(),
291 mValidatedFontCache(),
292 mFontDescriptionCache(),
293 mCharacterSetCache(),
294 mFontDescriptionSizeCache(),
298 mEmbeddedItemCache(),
299 mGlyphCacheManager(new GlyphCacheManager(GetMaxNumberOfGlyphCache())),
300 mLatestFoundFontDescription(),
301 mLatestFoundFontDescriptionId(0u),
302 mLatestFoundCacheKey(0, 0),
303 mLatestFoundCacheIndex(0u),
304 mDefaultFontDescriptionCached(false)
308 FontClient::Plugin::CacheHandler::~CacheHandler()
313 void FontClient::Plugin::CacheHandler::ClearCache()
315 // delete cached glyph informations before clear mFontFaceCache.
316 mGlyphCacheManager->ClearCache();
318 mDefaultFontDescription = FontDescription();
320 mSystemFonts.clear();
321 mDefaultFonts.clear();
323 DestroyCharacterSets(mDefaultFontCharacterSets);
324 mDefaultFontCharacterSets.Clear();
326 ClearFallbackCache();
327 mFallbackCache.clear();
329 mFontIdCache.clear();
331 ClearCharacterSetFromFontFaceCache();
332 mFontFaceCache.clear();
334 mValidatedFontCache.clear();
335 mFontDescriptionCache.clear();
337 DestroyCharacterSets(mCharacterSetCache);
338 mCharacterSetCache.Clear();
340 mFontDescriptionSizeCache.clear();
341 mFontDescriptionSizeCache.rehash(0); // Note : unordered_map.clear() didn't deallocate memory
343 mFontDataCache.clear();
344 mFontDataCache.rehash(0);
346 ClearFTFaceFromFontFTFaceCache();
347 mFontFTFaceCache.clear();
348 mFontFTFaceCache.rehash(0);
350 mEllipsisCache.clear();
351 mPixelBufferCache.clear();
352 mEmbeddedItemCache.clear();
353 mBitmapFontCache.clear();
355 mLatestFoundFontDescription.family.clear();
356 mLatestFoundCacheKey = FontDescriptionSizeCacheKey(0, 0);
358 mDefaultFontDescriptionCached = false;
361 void FontClient::Plugin::CacheHandler::ResetSystemDefaults()
363 mDefaultFontDescriptionCached = false;
368 void FontClient::Plugin::CacheHandler::ClearFallbackCache()
370 for(auto& item : mFallbackCache)
372 if(nullptr != item.fallbackFonts)
374 delete item.fallbackFonts;
377 if(nullptr != item.characterSets)
379 // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
380 DestroyCharacterSets(*item.characterSets);
381 delete item.characterSets;
386 void FontClient::Plugin::CacheHandler::ClearCharacterSetFromFontFaceCache()
388 for(auto& item : mFontFaceCache)
390 FcCharSetDestroy(item.mCharacterSet);
391 item.mCharacterSet = nullptr;
395 void FontClient::Plugin::CacheHandler::ClearCharacterSet()
397 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
398 DestroyCharacterSets(mDefaultFontCharacterSets);
399 DestroyCharacterSets(mCharacterSetCache);
400 mDefaultFontCharacterSets.Clear();
401 mCharacterSetCache.Clear();
403 for(auto& item : mFallbackCache)
405 // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
406 DestroyCharacterSets(*item.characterSets);
408 delete item.characterSets;
409 item.characterSets = nullptr;
412 // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
413 ClearCharacterSetFromFontFaceCache();
416 void FontClient::Plugin::CacheHandler::CreateCharacterSet()
418 for(const auto& description : mDefaultFonts)
420 mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
423 for(const auto& description : mFontDescriptionCache)
425 mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
428 for(auto& item : mFallbackCache)
430 if(nullptr != item.fallbackFonts)
432 if(nullptr == item.characterSets)
434 item.characterSets = new CharacterSetList;
437 for(const auto& description : *(item.fallbackFonts))
439 item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
445 void FontClient::Plugin::CacheHandler::ClearFTFaceFromFontFTFaceCache()
447 for(auto& item : mFontFTFaceCache)
449 FT_Done_Face(item.second);
455 void FontClient::Plugin::CacheHandler::InitSystemFonts()
457 if(mSystemFonts.empty())
459 FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
463 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont);
465 // Reserve some space to avoid reallocations.
466 mSystemFonts.reserve(fontSet->nfont);
468 for(int i = 0u; i < fontSet->nfont; ++i)
470 FcPattern* fontPattern = fontSet->fonts[i];
474 // Skip fonts with no path
475 if(GetFcString(fontPattern, FC_FILE, path))
477 mSystemFonts.push_back(FontDescription());
478 FontDescription& fontDescription = mSystemFonts.back();
480 fontDescription.path = std::move(path);
485 GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
486 GetFcInt(fontPattern, FC_WIDTH, width);
487 GetFcInt(fontPattern, FC_WEIGHT, weight);
488 GetFcInt(fontPattern, FC_SLANT, slant);
489 fontDescription.width = IntToWidthType(width);
490 fontDescription.weight = IntToWeightType(weight);
491 fontDescription.slant = IntToSlantType(slant);
493 FONT_LOG_DESCRIPTION(fontDescription, "system fonts");
497 // Destroys the font set created.
498 FcFontSetDestroy(fontSet);
503 void FontClient::Plugin::CacheHandler::InitDefaultFonts()
505 if(mDefaultFonts.empty())
507 FontDescription fontDescription;
508 fontDescription.family = DefaultFontFamily(); // todo This could be set to the Platform font
509 fontDescription.width = DefaultFontWidth();
510 fontDescription.weight = DefaultFontWeight();
511 fontDescription.slant = DefaultFontSlant();
512 SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
516 void FontClient::Plugin::CacheHandler::InitDefaultFontDescription()
518 if(!mDefaultFontDescriptionCached)
520 mDefaultFontDescriptionCached = true;
522 // Clear any font config stored info in the caches.
525 // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
526 FcInitReinitialize();
528 FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
530 if(nullptr != matchPattern)
532 FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
533 FcDefaultSubstitute(matchPattern);
535 FcCharSet* characterSet = nullptr;
536 bool matched = MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
538 // Caching the default font description
541 // Copy default font description info.
542 // Due to the type changed, we need to make some temperal font description.
543 FontDescription tempFontDescription = mDefaultFontDescription;
545 // Add the path to the cache.
546 tempFontDescription.type = FontDescription::FACE_FONT;
547 mFontDescriptionCache.push_back(tempFontDescription);
549 // Set the index to the vector of paths to font file names.
550 const FontDescriptionId fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
552 FONT_LOG_DESCRIPTION(tempFontDescription, "default platform font");
553 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " default font fontDescriptionId : %d\n", fontDescriptionId);
555 // Cache the index and the matched font's description.
556 CacheValidateFont(std::move(tempFontDescription), fontDescriptionId);
560 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " default font validation failed for font [%s]\n", mDefaultFontDescription.family.c_str());
563 // Decrease the reference counter of the character set as it's not stored.
564 // Note. the cached default font description will increase reference counter by
565 // mFontDescriptionCache in CreateCharacterSet(). So we can decrease reference counter here.
566 FcCharSetDestroy(characterSet);
568 // Destroys the pattern created.
569 FcPatternDestroy(matchPattern);
572 // Create again the character sets as they are not valid after FcInitReinitialize()
573 CreateCharacterSet();
579 bool FontClient::Plugin::CacheHandler::FindFontData(const std::string& fontPath) const
581 auto it = mFontDataCache.find(fontPath);
582 if(it != mFontDataCache.end())
590 bool FontClient::Plugin::CacheHandler::FindFontData(const std::string& fontPath, uint8_t*& fontDataPtr, std::streampos& dataSize) const
592 auto it = mFontDataCache.find(fontPath);
593 if(it != mFontDataCache.end())
595 fontDataPtr = it->second.first.Begin();
596 dataSize = it->second.second;
603 bool FontClient::Plugin::CacheHandler::LoadFontDataFromFile(const std::string& fontPath, Dali::Vector<uint8_t>& fontDataBuffer, std::streampos& dataSize) const
605 if(Dali::FileLoader::ReadFile(fontPath, dataSize, fontDataBuffer, Dali::FileLoader::BINARY))
607 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "PreLoad font file buffer : %zu, size : %ld, path : %s\n", fontDataBuffer.Size(), static_cast<long>(dataSize), fontPath.c_str());
614 void FontClient::Plugin::CacheHandler::CacheFontData(const std::string& fontPath, Dali::Vector<uint8_t>& fontDataBuffer, std::streampos& dataSize)
616 mFontDataCache[fontPath] = std::make_pair(std::move(fontDataBuffer), dataSize);
619 bool FontClient::Plugin::CacheHandler::FindFontFace(const std::string& fontPath) const
621 auto it = mFontFTFaceCache.find(fontPath);
622 if(it != mFontFTFaceCache.end())
630 void FontClient::Plugin::CacheHandler::CacheFontFace(const std::string& fontPath, FT_Face ftFace)
632 mFontFTFaceCache[fontPath] = std::move(ftFace);
637 bool FontClient::Plugin::CacheHandler::FindValidatedFont(const FontDescription& fontDescription,
638 FontDescriptionId& fontDescriptionId)
640 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
641 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of validated fonts in the cache : %zu\n", mValidatedFontCache.size());
643 fontDescriptionId = 0u;
645 // Fast cut if inputed family is empty.
646 if(DALI_UNLIKELY(fontDescription.family.empty()))
648 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description not found / fontDescription.family is empty!\n");
652 // Heuristic optimize code : Compare with latest found item.
653 if((fontDescription.width == mLatestFoundFontDescription.width) &&
654 (fontDescription.weight == mLatestFoundFontDescription.weight) &&
655 (fontDescription.slant == mLatestFoundFontDescription.slant) &&
656 (fontDescription.family == mLatestFoundFontDescription.family))
658 fontDescriptionId = mLatestFoundFontDescriptionId;
660 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description same as latest, id : %d\n", fontDescriptionId);
664 for(const auto& item : mValidatedFontCache)
666 if((fontDescription.width == item.fontDescription.width) &&
667 (fontDescription.weight == item.fontDescription.weight) &&
668 (fontDescription.slant == item.fontDescription.slant) &&
669 (fontDescription.family == item.fontDescription.family))
671 fontDescriptionId = item.index;
673 mLatestFoundFontDescription = fontDescription;
674 mLatestFoundFontDescriptionId = fontDescriptionId;
676 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description found, id : %d\n", fontDescriptionId);
681 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " validated font description not found\n");
685 void FontClient::Plugin::CacheHandler::ValidateFont(const FontDescription& fontDescription,
686 FontDescriptionId& fontDescriptionId)
688 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
689 FONT_LOG_DESCRIPTION(fontDescription, "");
691 DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_VALIDATE_FONT");
693 // Create a font pattern.
694 FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
696 FontDescription description;
698 FcCharSet* characterSet = nullptr;
699 bool matched = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
700 FcPatternDestroy(fontFamilyPattern);
702 if(matched && (nullptr != characterSet))
704 #if defined(TRACE_ENABLED)
705 if(gTraceFilter && gTraceFilter->IsTraceEnabled())
707 DALI_LOG_DEBUG_INFO("DALI_TEXT_VALIDATE_FONT : FcFontMatch : %s, style : %s, %s, %s\n", fontDescription.family.c_str(), FontWidth::Name[fontDescription.width], FontWeight::Name[fontDescription.weight], FontSlant::Name[fontDescription.slant]);
711 // Add the path to the cache.
712 description.type = FontDescription::FACE_FONT;
713 mFontDescriptionCache.push_back(description);
715 // Set the index to the vector of paths to font file names.
716 fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
718 FONT_LOG_DESCRIPTION(description, "matched");
719 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fontDescriptionId : %d\n", fontDescriptionId);
721 // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
722 mCharacterSetCache.PushBack(characterSet);
724 if((fontDescription.family != description.family) ||
725 (fontDescription.width != description.width) ||
726 (fontDescription.weight != description.weight) ||
727 (fontDescription.slant != description.slant))
729 // Cache the given font's description if it's different than the matched.
730 CacheValidateFont(std::move(FontDescription(fontDescription)), fontDescriptionId);
733 // Cache the index and the matched font's description.
734 CacheValidateFont(std::move(description), fontDescriptionId);
738 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str());
742 void FontClient::Plugin::CacheHandler::CacheValidateFont(FontDescription&& fontDescription,
743 FontDescriptionId validatedFontId)
745 mValidatedFontCache.emplace_back(std::move(FontDescriptionCacheItem(fontDescription, validatedFontId)));
750 bool FontClient::Plugin::CacheHandler::FindFallbackFontList(const FontDescription& fontDescription,
752 CharacterSetList*& characterSetList) const
754 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
755 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %zu\n", mFallbackCache.size());
759 for(const auto& item : mFallbackCache)
761 if(!fontDescription.family.empty() &&
762 (fontDescription.family == item.fontDescription.family) &&
763 (fontDescription.width == item.fontDescription.width) &&
764 (fontDescription.weight == item.fontDescription.weight) &&
765 (fontDescription.slant == item.fontDescription.slant))
767 fontList = item.fallbackFonts;
768 characterSetList = item.characterSets;
770 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fallback font list found.\n");
775 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fallback font list not found.\n");
779 void FontClient::Plugin::CacheHandler::CacheFallbackFontList(FontDescription&& fontDescription,
781 CharacterSetList*& characterSetList)
783 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
785 DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_FALLBACK_FONTLIST");
787 #if defined(TRACE_ENABLED)
788 if(gTraceFilter && gTraceFilter->IsTraceEnabled())
790 DALI_LOG_DEBUG_INFO("DALI_TEXT_FALLBACK_FONTLIST : FcFontSort : %s\n", fontDescription.family.c_str());
794 fontList = new FontList;
795 characterSetList = new CharacterSetList;
797 SetFontList(fontDescription, *fontList, *characterSetList);
799 FontDescription appleColorEmoji;
800 appleColorEmoji.family = "Apple Color Emoji";
801 appleColorEmoji.width = fontDescription.width;
802 appleColorEmoji.weight = fontDescription.weight;
803 appleColorEmoji.slant = fontDescription.slant;
804 FontList emojiFontList;
805 CharacterSetList emojiCharSetList;
806 SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
808 std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
809 emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
810 *fontList = std::move(emojiFontList);
811 *characterSetList = std::move(emojiCharSetList);
814 // Add the font-list to the cache.
815 mFallbackCache.push_back(std::move(CacheHandler::FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
820 bool FontClient::Plugin::CacheHandler::FindFontByPath(const FontPath& path,
821 PointSize26Dot6 requestedPointSize,
823 FontId& fontId) const
825 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
826 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " path : [%s]\n", path.c_str());
827 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
828 DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, " number of fonts in the cache : %zu\n", mFontFaceCache.size());
831 for(const auto& cacheItem : mFontFaceCache)
833 if(cacheItem.mRequestedPointSize == requestedPointSize &&
834 cacheItem.mFaceIndex == faceIndex &&
835 cacheItem.mPath == path)
837 fontId = cacheItem.mFontId + 1u;
839 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font found, id : %d\n", fontId);
844 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font not found\n");
848 bool FontClient::Plugin::CacheHandler::FindFont(FontDescriptionId fontDescriptionId,
849 PointSize26Dot6 requestedPointSize,
850 FontCacheIndex& fontCacheIndex)
852 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
853 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " fontDescriptionId : %d\n", fontDescriptionId);
854 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize);
858 const FontDescriptionSizeCacheKey key(fontDescriptionId, requestedPointSize);
860 // Heuristic optimize code : Compare with latest found item.
861 if(key == mLatestFoundCacheKey)
863 fontCacheIndex = mLatestFoundCacheIndex;
865 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font same as latest, index of font cache : %d\n", fontCacheIndex);
869 const auto& iter = mFontDescriptionSizeCache.find(key);
870 if(iter != mFontDescriptionSizeCache.cend())
872 fontCacheIndex = iter->second;
874 mLatestFoundCacheKey = key;
875 mLatestFoundCacheIndex = fontCacheIndex;
877 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font found, index of font cache : %d\n", fontCacheIndex);
881 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font not found.\n");
885 void FontClient::Plugin::CacheHandler::CacheFontDescriptionSize(FontDescriptionId fontDescriptionId, PointSize26Dot6 requestedPointSize, FontCacheIndex fontCacheIndex)
887 mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
890 void FontClient::Plugin::CacheHandler::CacheFontPath(FT_Face ftFace, FontId fontId, PointSize26Dot6 requestedPointSize, const FontPath& path)
892 FontDescription description;
893 description.path = path;
894 description.family = std::move(FontFamily(ftFace->family_name));
895 description.weight = FontWeight::NONE;
896 description.width = FontWidth::NONE;
897 description.slant = FontSlant::NONE;
899 // Note FreeType doesn't give too much info to build a proper font style.
900 if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
902 description.slant = FontSlant::ITALIC;
904 if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
906 description.weight = FontWeight::BOLD;
909 FontDescriptionId fontDescriptionId = 0u;
910 if(!FindValidatedFont(description, fontDescriptionId))
912 // TODO : Due to the FontClient pattern match process, we cannot pass dali-toolkit UTC.
913 // Can't we use ValidateFont here?
915 // Use font config to validate the font's description.
916 ValidateFont(description, fontDescriptionId);
918 const FontCacheIndex fontCacheIndex = mFontIdCache[fontId - 1u].index;
919 mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCharacterSetCache[fontDescriptionId - 1u]); // Increases the reference counter.
921 // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
922 mFontDescriptionSizeCache.emplace(CacheHandler::FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
925 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
927 FcResult result = FcResultMatch;
928 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
930 FcCharSet* characterSet = nullptr;
931 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
933 const FontCacheIndex fontCacheIndex = mFontIdCache[fontId - 1u].index;
934 mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
936 // Destroys the created patterns.
937 FcPatternDestroy(match);
938 FcPatternDestroy(pattern);
940 // Add the path to the cache.
941 description.type = FontDescription::FACE_FONT;
942 mFontDescriptionCache.push_back(description);
944 // Set the index to the vector of paths to font file names.
945 fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
947 // Increase the reference counter and add the character set to the cache.
948 mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
950 // Cache the index and the font's description.
951 CacheValidateFont(std::move(description), fontDescriptionId);
953 // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
954 CacheFontDescriptionSize(fontDescriptionId, requestedPointSize, fontCacheIndex);
958 FontId FontClient::Plugin::CacheHandler::CacheFontFaceCacheItem(FontFaceCacheItem&& fontFaceCacheItem)
960 // Set the index to the font's id cache.
961 fontFaceCacheItem.mFontId = static_cast<FontId>(mFontIdCache.size());
963 // Create the font id item to cache.
964 FontIdCacheItem fontIdCacheItem;
965 fontIdCacheItem.type = FontDescription::FACE_FONT;
967 // Set the index to the FreeType font face cache.
968 fontIdCacheItem.index = static_cast<FontCacheIndex>(mFontFaceCache.size());
971 mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
972 mFontIdCache.emplace_back(std::move(fontIdCacheItem));
974 // Set the font id to be returned.
975 FontId fontId = static_cast<FontId>(mFontIdCache.size());
982 bool FontClient::Plugin::CacheHandler::FindEllipsis(PointSize26Dot6 requestedPointSize, EllipsisCacheIndex& ellipsisCacheIndex) const
984 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
985 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize);
987 ellipsisCacheIndex = 0u;
989 // First look into the cache if there is an ellipsis glyph for the requested point size.
990 for(const auto& item : mEllipsisCache)
992 if(item.requestedPointSize == requestedPointSize)
994 // Use the glyph in the cache.
995 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index);
996 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " font %d.\n", item.glyph.fontId);
997 ellipsisCacheIndex = item.index;
1004 FontClient::Plugin::CacheHandler::EllipsisCacheIndex FontClient::Plugin::CacheHandler::CacheEllipsis(EllipsisItem&& ellipsisItem)
1006 EllipsisCacheIndex ellipsisCacheIndex = static_cast<EllipsisCacheIndex>(mEllipsisCache.size());
1008 mEllipsisCache.emplace_back(std::move(ellipsisItem));
1010 return ellipsisCacheIndex;
1015 bool FontClient::Plugin::CacheHandler::FindBitmapFont(const FontFamily& bitmapFontFamily, FontId& fontId) const
1019 for(const auto& item : mBitmapFontCache)
1021 if(bitmapFontFamily == item.font.name)
1023 fontId = item.id + 1u;
1031 FontId FontClient::Plugin::CacheHandler::CacheBitmapFontCacheItem(BitmapFontCacheItem&& bitmapFontCacheItem)
1033 // Set the index to the font's id cache.
1034 bitmapFontCacheItem.id = static_cast<FontId>(mFontIdCache.size());
1036 // Create the font id item to cache.
1037 CacheHandler::FontIdCacheItem fontIdCacheItem;
1038 fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1040 // Set the index to the Bitmap font face cache.
1041 fontIdCacheItem.index = static_cast<FontCacheIndex>(mBitmapFontCache.size());
1044 mBitmapFontCache.emplace_back(std::move(bitmapFontCacheItem));
1045 mFontIdCache.emplace_back(std::move(fontIdCacheItem));
1047 // Set the font id to be returned.
1048 FontId fontId = static_cast<FontId>(mFontIdCache.size());
1055 bool FontClient::Plugin::CacheHandler::FindEmbeddedPixelBufferId(const std::string& url, PixelBufferId& pixelBufferId) const
1059 for(const auto& cacheItem : mPixelBufferCache)
1061 if(cacheItem.url == url)
1063 // The url is in the pixel buffer cache.
1064 pixelBufferId = cacheItem.id;
1072 PixelBufferId FontClient::Plugin::CacheHandler::CacheEmbeddedPixelBuffer(const std::string& url)
1074 PixelBufferId pixelBufferId = 0u;
1076 // Load the image from the url.
1077 Devel::PixelBuffer pixelBuffer = LoadImageFromFile(url);
1080 // Create the cache item.
1081 PixelBufferCacheItem pixelBufferCacheItem;
1082 pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1083 pixelBufferCacheItem.url = url;
1084 pixelBufferCacheItem.id = static_cast<PixelBufferId>(mPixelBufferCache.size() + 1u);
1086 // Store the cache item in the cache.
1087 mPixelBufferCache.emplace_back(std::move(pixelBufferCacheItem));
1089 // Set the pixel buffer id to be returned.
1090 pixelBufferId = static_cast<PixelBufferId>(mPixelBufferCache.size());
1092 return pixelBufferId;
1095 bool FontClient::Plugin::CacheHandler::FindEmbeddedItem(PixelBufferId pixelBufferId, uint32_t width, uint32_t height, GlyphIndex& index) const
1099 for(const auto& cacheItem : mEmbeddedItemCache)
1101 if((cacheItem.pixelBufferId == pixelBufferId) &&
1102 (cacheItem.width == width) &&
1103 (cacheItem.height == height))
1105 index = cacheItem.index;
1113 GlyphIndex FontClient::Plugin::CacheHandler::CacheEmbeddedItem(EmbeddedItem&& embeddedItem)
1115 embeddedItem.index = static_cast<GlyphIndex>(mEmbeddedItemCache.size() + 1u);
1117 // Cache the embedded item.
1118 mEmbeddedItemCache.emplace_back(std::move(embeddedItem));
1120 // Set the font id to be returned.
1121 GlyphIndex index = static_cast<GlyphIndex>(mEmbeddedItemCache.size());
1126 } // namespace Dali::TextAbstraction::Internal