[Tizen] Ensure join of font thread
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-client-plugin-cache-handler.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/trace.h>
24 #include <fontconfig/fontconfig.h>
25
26 // INTERNAL INCLUDES
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>
34
35 // Use this macro only if need to log messages before the log function is set.
36 #define FONT_LOG_MESSAGE(level, format, ...)                                    \
37   do                                                                            \
38   {                                                                             \
39     char buffer[256];                                                           \
40     int  result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
41     if(result >= static_cast<int>(sizeof(buffer)))                              \
42     {                                                                           \
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);      \
45       break;                                                                    \
46     }                                                                           \
47     std::string log(buffer);                                                    \
48     Dali::TizenPlatform::LogMessage(level, log);                                \
49   } while(0)
50
51 #if defined(DEBUG_ENABLED)
52 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
53
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,                                                                            \
57                 "                 path : [%s]\n"                                                                                 \
58                 "                width : [%s]\n"                                                                                 \
59                 "               weight : [%s]\n"                                                                                 \
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])
65
66 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
67   DALI_LOG_INFO(gFontClientLogFilter, Debug::General,               \
68                 "           character : %p\n"                       \
69                 "  requestedPointSize : %d\n"                       \
70                 "         preferColor : %s\n",                      \
71                 charcode,                                           \
72                 requestedPointSize,                                 \
73                 (preferColor ? "true" : "false"))
74
75 #else
76
77 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
78 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
79
80 #endif
81
82 namespace
83 {
84
85 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_FONT_PERFORMANCE_MARKER, false);
86
87 /**
88  * @brief Maximum size of glyph cache per each font face.
89  */
90 constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX         = 128;
91 constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u;
92
93 constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
94
95 /**
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.
100  */
101 inline size_t GetMaxNumberOfGlyphCache()
102 {
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;
107 }
108
109 } // namespace
110
111 namespace Dali::TextAbstraction::Internal
112 {
113 namespace
114 {
115 /**
116  * @brief Free the resources allocated by the FcCharSet objects.
117  *
118  * @param[in] characterSets The vector of character sets.
119  */
120 void DestroyCharacterSets(CharacterSetList& characterSets)
121 {
122   for(auto& item : characterSets)
123   {
124     if(item)
125     {
126       FcCharSetDestroy(item);
127     }
128   }
129 }
130
131 /**
132  * @brief Retrieves the fonts present in the platform.
133  *
134  * @note Need to call FcFontSetDestroy to free the allocated resources.
135  *
136  * @return A font fonfig data structure with the platform's fonts.
137  */
138 _FcFontSet* GetFcFontSet()
139 {
140   FcFontSet* fontset = nullptr;
141
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();
145
146   if(nullptr != pattern)
147   {
148     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
149     FcObjectSet* objectSet = FcObjectSetCreate();
150
151     if(nullptr != objectSet)
152     {
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);
159
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.
163
164       // clear up the object set
165       FcObjectSetDestroy(objectSet);
166     }
167
168     // clear up the pattern
169     FcPatternDestroy(pattern);
170   }
171
172   return fontset;
173 }
174
175 /**
176  * @brief Helper for GetDefaultFonts etc.
177  *
178  * @note CharacterSetList is a vector of FcCharSet that are reference counted. It's needed to call FcCharSetDestroy to decrease the reference counter.
179  *
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.
183  */
184 void SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
185 {
186   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
187   fontList.clear();
188
189   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
190
191   FcResult result = FcResultMatch;
192
193   // Match the pattern.
194   FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
195                                   fontFamilyPattern,
196                                   false /* don't trim */,
197                                   nullptr,
198                                   &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
199
200   if(nullptr != fontSet)
201   {
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);
205
206     for(int i = 0u; i < fontSet->nfont; ++i)
207     {
208       FcPattern* fontPattern = fontSet->fonts[i];
209
210       FontPath path;
211
212       // Skip fonts with no path
213       if(GetFcString(fontPattern, FC_FILE, path))
214       {
215         // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
216         FcCharSet* characterSet = nullptr;
217         FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
218
219         // Increase the reference counter of the character set.
220         characterSetList.PushBack(FcCharSetCopy(characterSet));
221
222         fontList.push_back(FontDescription());
223         FontDescription& newFontDescription = fontList.back();
224
225         newFontDescription.path = std::move(path);
226
227         int width  = 0;
228         int weight = 0;
229         int slant  = 0;
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);
237
238         FONT_LOG_DESCRIPTION(newFontDescription, "new font");
239       }
240     }
241
242     // Destroys the font set created by FcFontSort.
243     FcFontSetDestroy(fontSet);
244   }
245   else
246   {
247     DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  No fonts found.\n");
248   }
249
250   // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
251   FcPatternDestroy(fontFamilyPattern);
252 }
253
254 } // namespace
255
256 FontClient::Plugin::CacheHandler::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
257 : fontDescription{std::move(font)},
258   fallbackFonts{fallbackFonts},
259   characterSets{characterSets}
260 {
261 }
262
263 FontClient::Plugin::CacheHandler::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
264                                                                                      FontDescriptionId      index)
265 : fontDescription{fontDescription},
266   index{index}
267 {
268 }
269
270 FontClient::Plugin::CacheHandler::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
271                                                                                      FontDescriptionId index)
272 : fontDescription{std::move(fontDescription)},
273   index{index}
274 {
275 }
276
277 FontClient::Plugin::CacheHandler::FontDescriptionSizeCacheKey::FontDescriptionSizeCacheKey(FontDescriptionId fontDescriptionId,
278                                                                                            PointSize26Dot6   requestedPointSize)
279 : fontDescriptionId(fontDescriptionId),
280   requestedPointSize(requestedPointSize)
281 {
282 }
283
284 // CacheHandler
285 FontClient::Plugin::CacheHandler::CacheHandler()
286 : mDefaultFontDescription(),
287   mSystemFonts(),
288   mDefaultFonts(),
289   mFontIdCache(),
290   mFontFaceCache(),
291   mValidatedFontCache(),
292   mFontDescriptionCache(),
293   mCharacterSetCache(),
294   mFontDescriptionSizeCache(),
295   mFontDataCache(),
296   mFontFTFaceCache(),
297   mEllipsisCache(),
298   mEmbeddedItemCache(),
299   mGlyphCacheManager(new GlyphCacheManager(GetMaxNumberOfGlyphCache())),
300   mLatestFoundFontDescription(),
301   mLatestFoundFontDescriptionId(0u),
302   mLatestFoundCacheKey(0, 0),
303   mLatestFoundCacheIndex(0u),
304   mDefaultFontDescriptionCached(false)
305 {
306 }
307
308 FontClient::Plugin::CacheHandler::~CacheHandler()
309 {
310   ClearCache();
311 }
312
313 void FontClient::Plugin::CacheHandler::ClearCache()
314 {
315   // delete cached glyph informations before clear mFontFaceCache.
316   mGlyphCacheManager->ClearCache();
317
318   mDefaultFontDescription = FontDescription();
319
320   mSystemFonts.clear();
321   mDefaultFonts.clear();
322
323   DestroyCharacterSets(mDefaultFontCharacterSets);
324   mDefaultFontCharacterSets.Clear();
325
326   ClearFallbackCache();
327   mFallbackCache.clear();
328
329   mFontIdCache.clear();
330
331   ClearCharacterSetFromFontFaceCache();
332   mFontFaceCache.clear();
333
334   mValidatedFontCache.clear();
335   mFontDescriptionCache.clear();
336
337   DestroyCharacterSets(mCharacterSetCache);
338   mCharacterSetCache.Clear();
339
340   mFontDescriptionSizeCache.clear();
341   mFontDescriptionSizeCache.rehash(0); // Note : unordered_map.clear() didn't deallocate memory
342
343   mFontDataCache.clear();
344   mFontDataCache.rehash(0);
345
346   ClearFTFaceFromFontFTFaceCache();
347   mFontFTFaceCache.clear();
348   mFontFTFaceCache.rehash(0);
349
350   mEllipsisCache.clear();
351   mPixelBufferCache.clear();
352   mEmbeddedItemCache.clear();
353   mBitmapFontCache.clear();
354
355   mLatestFoundFontDescription.family.clear();
356   mLatestFoundCacheKey = FontDescriptionSizeCacheKey(0, 0);
357
358   mDefaultFontDescriptionCached = false;
359 }
360
361 void FontClient::Plugin::CacheHandler::ResetSystemDefaults()
362 {
363   mDefaultFontDescriptionCached = false;
364 }
365
366 // Clear cache area
367
368 void FontClient::Plugin::CacheHandler::ClearFallbackCache()
369 {
370   for(auto& item : mFallbackCache)
371   {
372     if(nullptr != item.fallbackFonts)
373     {
374       delete item.fallbackFonts;
375     }
376
377     if(nullptr != item.characterSets)
378     {
379       // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
380       DestroyCharacterSets(*item.characterSets);
381       delete item.characterSets;
382     }
383   }
384 }
385
386 void FontClient::Plugin::CacheHandler::ClearCharacterSetFromFontFaceCache()
387 {
388   for(auto& item : mFontFaceCache)
389   {
390     FcCharSetDestroy(item.mCharacterSet);
391     item.mCharacterSet = nullptr;
392   }
393 }
394
395 void FontClient::Plugin::CacheHandler::ClearCharacterSet()
396 {
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();
402
403   for(auto& item : mFallbackCache)
404   {
405     // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
406     DestroyCharacterSets(*item.characterSets);
407
408     delete item.characterSets;
409     item.characterSets = nullptr;
410   }
411
412   // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
413   ClearCharacterSetFromFontFaceCache();
414 }
415
416 void FontClient::Plugin::CacheHandler::CreateCharacterSet()
417 {
418   for(const auto& description : mDefaultFonts)
419   {
420     mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
421   }
422
423   for(const auto& description : mFontDescriptionCache)
424   {
425     mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
426   }
427
428   for(auto& item : mFallbackCache)
429   {
430     if(nullptr != item.fallbackFonts)
431     {
432       if(nullptr == item.characterSets)
433       {
434         item.characterSets = new CharacterSetList;
435       }
436
437       for(const auto& description : *(item.fallbackFonts))
438       {
439         item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
440       }
441     }
442   }
443 }
444
445 void FontClient::Plugin::CacheHandler::ClearFTFaceFromFontFTFaceCache()
446 {
447   for(auto& item : mFontFTFaceCache)
448   {
449     FT_Done_Face(item.second);
450   }
451 }
452
453 // System / Default
454
455 void FontClient::Plugin::CacheHandler::InitSystemFonts()
456 {
457   if(mSystemFonts.empty())
458   {
459     FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
460
461     if(fontSet)
462     {
463       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont);
464
465       // Reserve some space to avoid reallocations.
466       mSystemFonts.reserve(fontSet->nfont);
467
468       for(int i = 0u; i < fontSet->nfont; ++i)
469       {
470         FcPattern* fontPattern = fontSet->fonts[i];
471
472         FontPath path;
473
474         // Skip fonts with no path
475         if(GetFcString(fontPattern, FC_FILE, path))
476         {
477           mSystemFonts.push_back(FontDescription());
478           FontDescription& fontDescription = mSystemFonts.back();
479
480           fontDescription.path = std::move(path);
481
482           int width  = 0;
483           int weight = 0;
484           int slant  = 0;
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);
492
493           FONT_LOG_DESCRIPTION(fontDescription, "system fonts");
494         }
495       }
496
497       // Destroys the font set created.
498       FcFontSetDestroy(fontSet);
499     }
500   }
501 }
502
503 void FontClient::Plugin::CacheHandler::InitDefaultFonts()
504 {
505   if(mDefaultFonts.empty())
506   {
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);
513   }
514 }
515
516 void FontClient::Plugin::CacheHandler::InitDefaultFontDescription()
517 {
518   if(!mDefaultFontDescriptionCached)
519   {
520     mDefaultFontDescriptionCached = true;
521
522     // Clear any font config stored info in the caches.
523     ClearCharacterSet();
524
525     // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
526     FcInitReinitialize();
527
528     FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
529
530     if(nullptr != matchPattern)
531     {
532       FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
533       FcDefaultSubstitute(matchPattern);
534
535       FcCharSet* characterSet = nullptr;
536       bool       matched      = MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
537
538       // Caching the default font description
539       if(matched)
540       {
541         // Copy default font description info.
542         // Due to the type changed, we need to make some temperal font description.
543         FontDescription tempFontDescription = mDefaultFontDescription;
544
545         // Add the path to the cache.
546         tempFontDescription.type = FontDescription::FACE_FONT;
547         mFontDescriptionCache.push_back(tempFontDescription);
548
549         // Set the index to the vector of paths to font file names.
550         const FontDescriptionId fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
551
552         FONT_LOG_DESCRIPTION(tempFontDescription, "default platform font");
553         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  default font fontDescriptionId : %d\n", fontDescriptionId);
554
555         // Cache the index and the matched font's description.
556         CacheValidateFont(std::move(tempFontDescription), fontDescriptionId);
557       }
558       else
559       {
560         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  default font validation failed for font [%s]\n", mDefaultFontDescription.family.c_str());
561       }
562
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);
567
568       // Destroys the pattern created.
569       FcPatternDestroy(matchPattern);
570     }
571
572     // Create again the character sets as they are not valid after FcInitReinitialize()
573     CreateCharacterSet();
574   }
575 }
576
577 // Font
578
579 bool FontClient::Plugin::CacheHandler::FindFontData(const std::string& fontPath) const
580 {
581   auto it = mFontDataCache.find(fontPath);
582   if(it != mFontDataCache.end())
583   {
584     return true;
585   }
586
587   return false;
588 }
589
590 bool FontClient::Plugin::CacheHandler::FindFontData(const std::string& fontPath, uint8_t*& fontDataPtr, std::streampos& dataSize) const
591 {
592   auto it = mFontDataCache.find(fontPath);
593   if(it != mFontDataCache.end())
594   {
595     fontDataPtr = it->second.first.Begin();
596     dataSize    = it->second.second;
597     return true;
598   }
599
600   return false;
601 }
602
603 bool FontClient::Plugin::CacheHandler::LoadFontDataFromFile(const std::string& fontPath, Dali::Vector<uint8_t>& fontDataBuffer, std::streampos& dataSize) const
604 {
605   if(Dali::FileLoader::ReadFile(fontPath, dataSize, fontDataBuffer, Dali::FileLoader::BINARY))
606   {
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());
608     return true;
609   }
610
611   return false;
612 }
613
614 void FontClient::Plugin::CacheHandler::CacheFontData(const std::string& fontPath, Dali::Vector<uint8_t>& fontDataBuffer, std::streampos& dataSize)
615 {
616   mFontDataCache[fontPath] = std::make_pair(std::move(fontDataBuffer), dataSize);
617 }
618
619 bool FontClient::Plugin::CacheHandler::FindFontFace(const std::string& fontPath) const
620 {
621   auto it = mFontFTFaceCache.find(fontPath);
622   if(it != mFontFTFaceCache.end())
623   {
624     return true;
625   }
626
627   return false;
628 }
629
630 void FontClient::Plugin::CacheHandler::CacheFontFace(const std::string& fontPath, FT_Face ftFace)
631 {
632   mFontFTFaceCache[fontPath] = std::move(ftFace);
633 }
634
635 // Validate
636
637 bool FontClient::Plugin::CacheHandler::FindValidatedFont(const FontDescription& fontDescription,
638                                                          FontDescriptionId&     fontDescriptionId)
639 {
640   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
641   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %zu\n", mValidatedFontCache.size());
642
643   fontDescriptionId = 0u;
644
645   // Fast cut if inputed family is empty.
646   if(DALI_UNLIKELY(fontDescription.family.empty()))
647   {
648     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description not found / fontDescription.family is empty!\n");
649     return false;
650   }
651
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))
657   {
658     fontDescriptionId = mLatestFoundFontDescriptionId;
659
660     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description same as latest, id : %d\n", fontDescriptionId);
661     return true;
662   }
663
664   for(const auto& item : mValidatedFontCache)
665   {
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))
670     {
671       fontDescriptionId = item.index;
672
673       mLatestFoundFontDescription   = fontDescription;
674       mLatestFoundFontDescriptionId = fontDescriptionId;
675
676       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description found, id : %d\n", fontDescriptionId);
677       return true;
678     }
679   }
680
681   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description not found\n");
682   return false;
683 }
684
685 void FontClient::Plugin::CacheHandler::ValidateFont(const FontDescription& fontDescription,
686                                                     FontDescriptionId&     fontDescriptionId)
687 {
688   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
689   FONT_LOG_DESCRIPTION(fontDescription, "");
690
691   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_VALIDATE_FONT");
692
693   // Create a font pattern.
694   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
695
696   FontDescription description;
697
698   FcCharSet* characterSet = nullptr;
699   bool       matched      = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
700   FcPatternDestroy(fontFamilyPattern);
701
702   if(matched && (nullptr != characterSet))
703   {
704     #if defined(TRACE_ENABLED)
705     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
706     {
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]);
708     }
709     #endif
710
711     // Add the path to the cache.
712     description.type = FontDescription::FACE_FONT;
713     mFontDescriptionCache.push_back(description);
714
715     // Set the index to the vector of paths to font file names.
716     fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
717
718     FONT_LOG_DESCRIPTION(description, "matched");
719     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fontDescriptionId : %d\n", fontDescriptionId);
720
721     // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
722     mCharacterSetCache.PushBack(characterSet);
723
724     if((fontDescription.family != description.family) ||
725        (fontDescription.width != description.width) ||
726        (fontDescription.weight != description.weight) ||
727        (fontDescription.slant != description.slant))
728     {
729       // Cache the given font's description if it's different than the matched.
730       CacheValidateFont(std::move(FontDescription(fontDescription)), fontDescriptionId);
731     }
732
733     // Cache the index and the matched font's description.
734     CacheValidateFont(std::move(description), fontDescriptionId);
735   }
736   else
737   {
738     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font validation failed for font [%s]\n", fontDescription.family.c_str());
739   }
740 }
741
742 void FontClient::Plugin::CacheHandler::CacheValidateFont(FontDescription&& fontDescription,
743                                                          FontDescriptionId validatedFontId)
744 {
745   mValidatedFontCache.emplace_back(std::move(FontDescriptionCacheItem(fontDescription, validatedFontId)));
746 }
747
748 // Fallback
749
750 bool FontClient::Plugin::CacheHandler::FindFallbackFontList(const FontDescription& fontDescription,
751                                                             FontList*&             fontList,
752                                                             CharacterSetList*&     characterSetList) const
753 {
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());
756
757   fontList = nullptr;
758
759   for(const auto& item : mFallbackCache)
760   {
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))
766     {
767       fontList         = item.fallbackFonts;
768       characterSetList = item.characterSets;
769
770       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list found.\n");
771       return true;
772     }
773   }
774
775   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list not found.\n");
776   return false;
777 }
778
779 void FontClient::Plugin::CacheHandler::CacheFallbackFontList(FontDescription&&  fontDescription,
780                                                              FontList*&         fontList,
781                                                              CharacterSetList*& characterSetList)
782 {
783   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
784
785   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_FALLBACK_FONTLIST");
786
787   #if defined(TRACE_ENABLED)
788   if(gTraceFilter && gTraceFilter->IsTraceEnabled())
789   {
790     DALI_LOG_DEBUG_INFO("DALI_TEXT_FALLBACK_FONTLIST : FcFontSort : %s\n", fontDescription.family.c_str());
791   }
792   #endif
793
794   fontList         = new FontList;
795   characterSetList = new CharacterSetList;
796
797   SetFontList(fontDescription, *fontList, *characterSetList);
798 #ifdef __APPLE__
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);
807
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);
812 #endif
813
814   // Add the font-list to the cache.
815   mFallbackCache.push_back(std::move(CacheHandler::FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
816 }
817
818 // Font / FontFace
819
820 bool FontClient::Plugin::CacheHandler::FindFontByPath(const FontPath& path,
821                                                       PointSize26Dot6 requestedPointSize,
822                                                       FaceIndex       faceIndex,
823                                                       FontId&         fontId) const
824 {
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());
829
830   fontId = 0u;
831   for(const auto& cacheItem : mFontFaceCache)
832   {
833     if(cacheItem.mRequestedPointSize == requestedPointSize &&
834        cacheItem.mFaceIndex == faceIndex &&
835        cacheItem.mPath == path)
836     {
837       fontId = cacheItem.mFontId + 1u;
838
839       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, id : %d\n", fontId);
840       return true;
841     }
842   }
843
844   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found\n");
845   return false;
846 }
847
848 bool FontClient::Plugin::CacheHandler::FindFont(FontDescriptionId fontDescriptionId,
849                                                 PointSize26Dot6   requestedPointSize,
850                                                 FontCacheIndex&   fontCacheIndex)
851 {
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);
855
856   fontCacheIndex = 0u;
857
858   const FontDescriptionSizeCacheKey key(fontDescriptionId, requestedPointSize);
859
860   // Heuristic optimize code : Compare with latest found item.
861   if(key == mLatestFoundCacheKey)
862   {
863     fontCacheIndex = mLatestFoundCacheIndex;
864
865     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font same as latest, index of font cache : %d\n", fontCacheIndex);
866     return true;
867   }
868
869   const auto& iter = mFontDescriptionSizeCache.find(key);
870   if(iter != mFontDescriptionSizeCache.cend())
871   {
872     fontCacheIndex = iter->second;
873
874     mLatestFoundCacheKey   = key;
875     mLatestFoundCacheIndex = fontCacheIndex;
876
877     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, index of font cache : %d\n", fontCacheIndex);
878     return true;
879   }
880
881   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found.\n");
882   return false;
883 }
884
885 void FontClient::Plugin::CacheHandler::CacheFontDescriptionSize(FontDescriptionId fontDescriptionId, PointSize26Dot6 requestedPointSize, FontCacheIndex fontCacheIndex)
886 {
887   mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
888 }
889
890 void FontClient::Plugin::CacheHandler::CacheFontPath(FT_Face ftFace, FontId fontId, PointSize26Dot6 requestedPointSize, const FontPath& path)
891 {
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;
898
899   // Note FreeType doesn't give too much info to build a proper font style.
900   if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
901   {
902     description.slant = FontSlant::ITALIC;
903   }
904   if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
905   {
906     description.weight = FontWeight::BOLD;
907   }
908
909   FontDescriptionId fontDescriptionId = 0u;
910   if(!FindValidatedFont(description, fontDescriptionId))
911   {
912     // TODO : Due to the FontClient pattern match process, we cannot pass dali-toolkit UTC.
913     // Can't we use ValidateFont here?
914     /*
915     // Use font config to validate the font's description.
916     ValidateFont(description, fontDescriptionId);
917
918     const FontCacheIndex fontCacheIndex          = mFontIdCache[fontId - 1u].index;
919     mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCharacterSetCache[fontDescriptionId - 1u]); // Increases the reference counter.
920
921     // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
922     mFontDescriptionSizeCache.emplace(CacheHandler::FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
923     */
924
925     FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
926
927     FcResult   result = FcResultMatch;
928     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
929
930     FcCharSet* characterSet = nullptr;
931     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
932
933     const FontCacheIndex fontCacheIndex          = mFontIdCache[fontId - 1u].index;
934     mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
935
936     // Destroys the created patterns.
937     FcPatternDestroy(match);
938     FcPatternDestroy(pattern);
939
940     // Add the path to the cache.
941     description.type = FontDescription::FACE_FONT;
942     mFontDescriptionCache.push_back(description);
943
944     // Set the index to the vector of paths to font file names.
945     fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
946
947     // Increase the reference counter and add the character set to the cache.
948     mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
949
950     // Cache the index and the font's description.
951     CacheValidateFont(std::move(description), fontDescriptionId);
952
953     // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
954     CacheFontDescriptionSize(fontDescriptionId, requestedPointSize, fontCacheIndex);
955   }
956 }
957
958 FontId FontClient::Plugin::CacheHandler::CacheFontFaceCacheItem(FontFaceCacheItem&& fontFaceCacheItem)
959 {
960   // Set the index to the font's id cache.
961   fontFaceCacheItem.mFontId = static_cast<FontId>(mFontIdCache.size());
962
963   // Create the font id item to cache.
964   FontIdCacheItem fontIdCacheItem;
965   fontIdCacheItem.type = FontDescription::FACE_FONT;
966
967   // Set the index to the FreeType font face cache.
968   fontIdCacheItem.index = static_cast<FontCacheIndex>(mFontFaceCache.size());
969
970   // Cache the items.
971   mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
972   mFontIdCache.emplace_back(std::move(fontIdCacheItem));
973
974   // Set the font id to be returned.
975   FontId fontId = static_cast<FontId>(mFontIdCache.size());
976
977   return fontId;
978 }
979
980 // Ellipsis
981
982 bool FontClient::Plugin::CacheHandler::FindEllipsis(PointSize26Dot6 requestedPointSize, EllipsisCacheIndex& ellipsisCacheIndex) const
983 {
984   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
985   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize);
986
987   ellipsisCacheIndex = 0u;
988
989   // First look into the cache if there is an ellipsis glyph for the requested point size.
990   for(const auto& item : mEllipsisCache)
991   {
992     if(item.requestedPointSize == requestedPointSize)
993     {
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;
998       return true;
999     }
1000   }
1001   return false;
1002 }
1003
1004 FontClient::Plugin::CacheHandler::EllipsisCacheIndex FontClient::Plugin::CacheHandler::CacheEllipsis(EllipsisItem&& ellipsisItem)
1005 {
1006   EllipsisCacheIndex ellipsisCacheIndex = static_cast<EllipsisCacheIndex>(mEllipsisCache.size());
1007
1008   mEllipsisCache.emplace_back(std::move(ellipsisItem));
1009
1010   return ellipsisCacheIndex;
1011 }
1012
1013 // Bitmap font
1014
1015 bool FontClient::Plugin::CacheHandler::FindBitmapFont(const FontFamily& bitmapFontFamily, FontId& fontId) const
1016 {
1017   fontId = 0u;
1018
1019   for(const auto& item : mBitmapFontCache)
1020   {
1021     if(bitmapFontFamily == item.font.name)
1022     {
1023       fontId = item.id + 1u;
1024       return true;
1025     }
1026   }
1027
1028   return false;
1029 }
1030
1031 FontId FontClient::Plugin::CacheHandler::CacheBitmapFontCacheItem(BitmapFontCacheItem&& bitmapFontCacheItem)
1032 {
1033   // Set the index to the font's id cache.
1034   bitmapFontCacheItem.id = static_cast<FontId>(mFontIdCache.size());
1035
1036   // Create the font id item to cache.
1037   CacheHandler::FontIdCacheItem fontIdCacheItem;
1038   fontIdCacheItem.type = FontDescription::BITMAP_FONT;
1039
1040   // Set the index to the Bitmap font face cache.
1041   fontIdCacheItem.index = static_cast<FontCacheIndex>(mBitmapFontCache.size());
1042
1043   // Cache the items.
1044   mBitmapFontCache.emplace_back(std::move(bitmapFontCacheItem));
1045   mFontIdCache.emplace_back(std::move(fontIdCacheItem));
1046
1047   // Set the font id to be returned.
1048   FontId fontId = static_cast<FontId>(mFontIdCache.size());
1049
1050   return fontId;
1051 }
1052
1053 // Embedded
1054
1055 bool FontClient::Plugin::CacheHandler::FindEmbeddedPixelBufferId(const std::string& url, PixelBufferId& pixelBufferId) const
1056 {
1057   pixelBufferId = 0u;
1058
1059   for(const auto& cacheItem : mPixelBufferCache)
1060   {
1061     if(cacheItem.url == url)
1062     {
1063       // The url is in the pixel buffer cache.
1064       pixelBufferId = cacheItem.id;
1065       return true;
1066     }
1067   }
1068
1069   return false;
1070 }
1071
1072 PixelBufferId FontClient::Plugin::CacheHandler::CacheEmbeddedPixelBuffer(const std::string& url)
1073 {
1074   PixelBufferId pixelBufferId = 0u;
1075
1076   // Load the image from the url.
1077   Devel::PixelBuffer pixelBuffer = LoadImageFromFile(url);
1078   if(pixelBuffer)
1079   {
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);
1085
1086     // Store the cache item in the cache.
1087     mPixelBufferCache.emplace_back(std::move(pixelBufferCacheItem));
1088
1089     // Set the pixel buffer id to be returned.
1090     pixelBufferId = static_cast<PixelBufferId>(mPixelBufferCache.size());
1091   }
1092   return pixelBufferId;
1093 }
1094
1095 bool FontClient::Plugin::CacheHandler::FindEmbeddedItem(PixelBufferId pixelBufferId, uint32_t width, uint32_t height, GlyphIndex& index) const
1096 {
1097   index = 0u;
1098
1099   for(const auto& cacheItem : mEmbeddedItemCache)
1100   {
1101     if((cacheItem.pixelBufferId == pixelBufferId) &&
1102        (cacheItem.width == width) &&
1103        (cacheItem.height == height))
1104     {
1105       index = cacheItem.index;
1106       return true;
1107     }
1108   }
1109
1110   return false;
1111 }
1112
1113 GlyphIndex FontClient::Plugin::CacheHandler::CacheEmbeddedItem(EmbeddedItem&& embeddedItem)
1114 {
1115   embeddedItem.index = static_cast<GlyphIndex>(mEmbeddedItemCache.size() + 1u);
1116
1117   // Cache the embedded item.
1118   mEmbeddedItemCache.emplace_back(std::move(embeddedItem));
1119
1120   // Set the font id to be returned.
1121   GlyphIndex index = static_cast<GlyphIndex>(mEmbeddedItemCache.size());
1122
1123   return index;
1124 }
1125
1126 } // namespace Dali::TextAbstraction::Internal