b0a0527b30dd480482bae2292822be34e8248ce7
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-client-plugin-cache-handler.cpp
1 /*
2  * Copyright (c) 2022 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 <fontconfig/fontconfig.h>
24
25 // INTERNAL INCLUDES
26 #include <dali/devel-api/adaptor-framework/environment-variable.h>
27 #include <dali/devel-api/adaptor-framework/image-loading.h>
28 #include <dali/internal/text/text-abstraction/font-client-impl.h>
29 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
30 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
31
32 #if defined(DEBUG_ENABLED)
33 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
34
35 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)                                                                            \
36   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix "  description; family : [%s]\n", fontDescription.family.c_str()); \
37   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose,                                                                            \
38                 "                 path : [%s]\n"                                                                                 \
39                 "                width : [%s]\n"                                                                                 \
40                 "               weight : [%s]\n"                                                                                 \
41                 "                slant : [%s]\n\n",                                                                              \
42                 fontDescription.path.c_str(),                                                                                    \
43                 FontWidth::Name[fontDescription.width],                                                                          \
44                 FontWeight::Name[fontDescription.weight],                                                                        \
45                 FontSlant::Name[fontDescription.slant])
46
47 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
48   DALI_LOG_INFO(gFontClientLogFilter, Debug::General,               \
49                 "           character : %p\n"                       \
50                 "  requestedPointSize : %d\n"                       \
51                 "         preferColor : %s\n",                      \
52                 charcode,                                           \
53                 requestedPointSize,                                 \
54                 (preferColor ? "true" : "false"))
55
56 #else
57
58 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
59 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
60
61 #endif
62
63 namespace
64 {
65 /**
66  * @brief Maximum size of glyph cache per each font face.
67  */
68 constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX         = 128;
69 constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u;
70
71 constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
72
73 /**
74  * @brief Get maximum size of glyph cache size from environment.
75  * If not settuped, default as 128.
76  * @note This value fixed when we call it first time.
77  * @return The max size of glyph cache.
78  */
79 inline size_t GetMaxNumberOfGlyphCache()
80 {
81   using Dali::EnvironmentVariable::GetEnvironmentVariable;
82   static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV);
83   static auto number       = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX;
84   return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number;
85 }
86
87 } // namespace
88
89 namespace Dali::TextAbstraction::Internal
90 {
91 namespace
92 {
93 /**
94  * @brief Free the resources allocated by the FcCharSet objects.
95  *
96  * @param[in] characterSets The vector of character sets.
97  */
98 void DestroyCharacterSets(CharacterSetList& characterSets)
99 {
100   for(auto& item : characterSets)
101   {
102     if(item)
103     {
104       FcCharSetDestroy(item);
105     }
106   }
107 }
108
109 /**
110  * @brief Retrieves the fonts present in the platform.
111  *
112  * @note Need to call FcFontSetDestroy to free the allocated resources.
113  *
114  * @return A font fonfig data structure with the platform's fonts.
115  */
116 _FcFontSet* GetFcFontSet()
117 {
118   FcFontSet* fontset = nullptr;
119
120   // create a new pattern.
121   // a pattern holds a set of names, each name refers to a property of the font
122   FcPattern* pattern = FcPatternCreate();
123
124   if(nullptr != pattern)
125   {
126     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
127     FcObjectSet* objectSet = FcObjectSetCreate();
128
129     if(nullptr != objectSet)
130     {
131       // build an object set from a list of property names
132       FcObjectSetAdd(objectSet, FC_FILE);
133       FcObjectSetAdd(objectSet, FC_FAMILY);
134       FcObjectSetAdd(objectSet, FC_WIDTH);
135       FcObjectSetAdd(objectSet, FC_WEIGHT);
136       FcObjectSetAdd(objectSet, FC_SLANT);
137
138       // get a list of fonts
139       // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
140       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.
141
142       // clear up the object set
143       FcObjectSetDestroy(objectSet);
144     }
145
146     // clear up the pattern
147     FcPatternDestroy(pattern);
148   }
149
150   return fontset;
151 }
152
153 /**
154  * @brief Helper for GetDefaultFonts etc.
155  *
156  * @note CharacterSetList is a vector of FcCharSet that are reference counted. It's needed to call FcCharSetDestroy to decrease the reference counter.
157  *
158  * @param[in] fontDescription A font description.
159  * @param[out] fontList A list of the fonts which are a close match for fontDescription.
160  * @param[out] characterSetList A list of character sets which are a close match for fontDescription.
161  */
162 void SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
163 {
164   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
165   fontList.clear();
166
167   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
168
169   FcResult result = FcResultMatch;
170
171   // Match the pattern.
172   FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
173                                   fontFamilyPattern,
174                                   false /* don't trim */,
175                                   nullptr,
176                                   &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
177
178   if(nullptr != fontSet)
179   {
180     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of fonts found : [%d]\n", fontSet->nfont);
181     // Reserve some space to avoid reallocations.
182     fontList.reserve(fontSet->nfont);
183
184     for(int i = 0u; i < fontSet->nfont; ++i)
185     {
186       FcPattern* fontPattern = fontSet->fonts[i];
187
188       FontPath path;
189
190       // Skip fonts with no path
191       if(GetFcString(fontPattern, FC_FILE, path))
192       {
193         // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
194         FcCharSet* characterSet = nullptr;
195         FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
196
197         // Increase the reference counter of the character set.
198         characterSetList.PushBack(FcCharSetCopy(characterSet));
199
200         fontList.push_back(FontDescription());
201         FontDescription& newFontDescription = fontList.back();
202
203         newFontDescription.path = std::move(path);
204
205         int width  = 0;
206         int weight = 0;
207         int slant  = 0;
208         GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
209         GetFcInt(fontPattern, FC_WIDTH, width);
210         GetFcInt(fontPattern, FC_WEIGHT, weight);
211         GetFcInt(fontPattern, FC_SLANT, slant);
212         newFontDescription.width  = IntToWidthType(width);
213         newFontDescription.weight = IntToWeightType(weight);
214         newFontDescription.slant  = IntToSlantType(slant);
215
216         FONT_LOG_DESCRIPTION(newFontDescription, "new font");
217       }
218     }
219
220     // Destroys the font set created by FcFontSort.
221     FcFontSetDestroy(fontSet);
222   }
223   else
224   {
225     DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  No fonts found.\n");
226   }
227
228   // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
229   FcPatternDestroy(fontFamilyPattern);
230 }
231
232 } // namespace
233
234 FontClient::Plugin::CacheHandler::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
235 : fontDescription{std::move(font)},
236   fallbackFonts{fallbackFonts},
237   characterSets{characterSets}
238 {
239 }
240
241 FontClient::Plugin::CacheHandler::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
242                                                                                      FontDescriptionId      index)
243 : fontDescription{fontDescription},
244   index{index}
245 {
246 }
247
248 FontClient::Plugin::CacheHandler::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
249                                                                                      FontDescriptionId index)
250 : fontDescription{std::move(fontDescription)},
251   index{index}
252 {
253 }
254
255 FontClient::Plugin::CacheHandler::FontDescriptionSizeCacheKey::FontDescriptionSizeCacheKey(FontDescriptionId fontDescriptionId,
256                                                                                            PointSize26Dot6   requestedPointSize)
257 : fontDescriptionId(fontDescriptionId),
258   requestedPointSize(requestedPointSize)
259 {
260 }
261
262 // CacheHandler
263 FontClient::Plugin::CacheHandler::CacheHandler()
264 : mDefaultFontDescription(),
265   mSystemFonts(),
266   mDefaultFonts(),
267   mFontIdCache(),
268   mFontFaceCache(),
269   mValidatedFontCache(),
270   mFontDescriptionCache(),
271   mCharacterSetCache(),
272   mFontDescriptionSizeCache(),
273   mEllipsisCache(),
274   mEmbeddedItemCache(),
275   mGlyphCacheManager(new GlyphCacheManager(GetMaxNumberOfGlyphCache())),
276   mLatestFoundFontDescription(),
277   mLatestFoundFontDescriptionId(0u),
278   mLatestFoundCacheKey(0, 0),
279   mLatestFoundCacheIndex(0u),
280   mDefaultFontDescriptionCached(false)
281 {
282 }
283
284 FontClient::Plugin::CacheHandler::~CacheHandler()
285 {
286   ClearCache();
287 }
288
289 void FontClient::Plugin::CacheHandler::ClearCache()
290 {
291   // delete cached glyph informations before clear mFontFaceCache.
292   mGlyphCacheManager->ClearCache();
293
294   mDefaultFontDescription = FontDescription();
295
296   mSystemFonts.clear();
297   mDefaultFonts.clear();
298
299   DestroyCharacterSets(mDefaultFontCharacterSets);
300   mDefaultFontCharacterSets.Clear();
301
302   ClearFallbackCache();
303   mFallbackCache.clear();
304
305   mFontIdCache.clear();
306
307   ClearCharacterSetFromFontFaceCache();
308   mFontFaceCache.clear();
309
310   mValidatedFontCache.clear();
311   mFontDescriptionCache.clear();
312
313   DestroyCharacterSets(mCharacterSetCache);
314   mCharacterSetCache.Clear();
315
316   mFontDescriptionSizeCache.clear();
317   mFontDescriptionSizeCache.rehash(0); // Note : unordered_map.clear() didn't deallocate memory
318
319   mEllipsisCache.clear();
320   mPixelBufferCache.clear();
321   mEmbeddedItemCache.clear();
322   mBitmapFontCache.clear();
323
324   mLatestFoundFontDescription.family.clear();
325   mLatestFoundCacheKey = FontDescriptionSizeCacheKey(0, 0);
326
327   mDefaultFontDescriptionCached = false;
328 }
329
330 void FontClient::Plugin::CacheHandler::ResetSystemDefaults()
331 {
332   mDefaultFontDescriptionCached = false;
333 }
334
335 // Clear cache area
336
337 void FontClient::Plugin::CacheHandler::ClearFallbackCache()
338 {
339   for(auto& item : mFallbackCache)
340   {
341     if(nullptr != item.fallbackFonts)
342     {
343       delete item.fallbackFonts;
344     }
345
346     if(nullptr != item.characterSets)
347     {
348       // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
349       DestroyCharacterSets(*item.characterSets);
350       delete item.characterSets;
351     }
352   }
353 }
354
355 void FontClient::Plugin::CacheHandler::ClearCharacterSetFromFontFaceCache()
356 {
357   for(auto& item : mFontFaceCache)
358   {
359     FcCharSetDestroy(item.mCharacterSet);
360     item.mCharacterSet = nullptr;
361   }
362 }
363
364 void FontClient::Plugin::CacheHandler::ClearCharacterSet()
365 {
366   // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
367   DestroyCharacterSets(mDefaultFontCharacterSets);
368   DestroyCharacterSets(mCharacterSetCache);
369   mDefaultFontCharacterSets.Clear();
370   mCharacterSetCache.Clear();
371
372   for(auto& item : mFallbackCache)
373   {
374     // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
375     DestroyCharacterSets(*item.characterSets);
376
377     delete item.characterSets;
378     item.characterSets = nullptr;
379   }
380
381   // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
382   ClearCharacterSetFromFontFaceCache();
383 }
384
385 void FontClient::Plugin::CacheHandler::CreateCharacterSet()
386 {
387   for(const auto& description : mDefaultFonts)
388   {
389     mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
390   }
391
392   for(const auto& description : mFontDescriptionCache)
393   {
394     mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
395   }
396
397   for(auto& item : mFallbackCache)
398   {
399     if(nullptr != item.fallbackFonts)
400     {
401       if(nullptr == item.characterSets)
402       {
403         item.characterSets = new CharacterSetList;
404       }
405
406       for(const auto& description : *(item.fallbackFonts))
407       {
408         item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
409       }
410     }
411   }
412 }
413
414 // System / Default
415
416 void FontClient::Plugin::CacheHandler::InitSystemFonts()
417 {
418   if(mSystemFonts.empty())
419   {
420     FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
421
422     if(fontSet)
423     {
424       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont);
425
426       // Reserve some space to avoid reallocations.
427       mSystemFonts.reserve(fontSet->nfont);
428
429       for(int i = 0u; i < fontSet->nfont; ++i)
430       {
431         FcPattern* fontPattern = fontSet->fonts[i];
432
433         FontPath path;
434
435         // Skip fonts with no path
436         if(GetFcString(fontPattern, FC_FILE, path))
437         {
438           mSystemFonts.push_back(FontDescription());
439           FontDescription& fontDescription = mSystemFonts.back();
440
441           fontDescription.path = std::move(path);
442
443           int width  = 0;
444           int weight = 0;
445           int slant  = 0;
446           GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
447           GetFcInt(fontPattern, FC_WIDTH, width);
448           GetFcInt(fontPattern, FC_WEIGHT, weight);
449           GetFcInt(fontPattern, FC_SLANT, slant);
450           fontDescription.width  = IntToWidthType(width);
451           fontDescription.weight = IntToWeightType(weight);
452           fontDescription.slant  = IntToSlantType(slant);
453
454           FONT_LOG_DESCRIPTION(fontDescription, "system fonts");
455         }
456       }
457
458       // Destroys the font set created.
459       FcFontSetDestroy(fontSet);
460     }
461   }
462 }
463
464 void FontClient::Plugin::CacheHandler::InitDefaultFonts()
465 {
466   if(mDefaultFonts.empty())
467   {
468     FontDescription fontDescription;
469     fontDescription.family = DefaultFontFamily(); // todo This could be set to the Platform font
470     fontDescription.width  = DefaultFontWidth();
471     fontDescription.weight = DefaultFontWeight();
472     fontDescription.slant  = DefaultFontSlant();
473     SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
474   }
475 }
476
477 void FontClient::Plugin::CacheHandler::InitDefaultFontDescription()
478 {
479   if(!mDefaultFontDescriptionCached)
480   {
481     // Clear any font config stored info in the caches.
482     ClearCharacterSet();
483
484     // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
485     FcInitReinitialize();
486
487     FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
488
489     if(nullptr != matchPattern)
490     {
491       FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
492       FcDefaultSubstitute(matchPattern);
493
494       FcCharSet* characterSet = nullptr;
495       bool       matched      = MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
496
497       // Caching the default font description
498       if(matched)
499       {
500         // Copy default font description info.
501         // Due to the type changed, we need to make some temperal font description.
502         FontDescription tempFontDescription = mDefaultFontDescription;
503
504         // Add the path to the cache.
505         tempFontDescription.type = FontDescription::FACE_FONT;
506         mFontDescriptionCache.push_back(tempFontDescription);
507
508         // Set the index to the vector of paths to font file names.
509         const FontDescriptionId fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
510
511         FONT_LOG_DESCRIPTION(tempFontDescription, "default platform font");
512         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  default font fontDescriptionId : %d\n", fontDescriptionId);
513
514         // Cache the index and the matched font's description.
515         CacheValidateFont(std::move(tempFontDescription), fontDescriptionId);
516       }
517       else
518       {
519         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  default font validation failed for font [%s]\n", mDefaultFontDescription.family.c_str());
520       }
521
522       // Decrease the reference counter of the character set as it's not stored.
523       // Note. the cached default font description will increase reference counter by
524       // mFontDescriptionCache in CreateCharacterSet(). So we can decrease reference counter here.
525       FcCharSetDestroy(characterSet);
526
527       // Destroys the pattern created.
528       FcPatternDestroy(matchPattern);
529     }
530
531     // Create again the character sets as they are not valid after FcInitReinitialize()
532     CreateCharacterSet();
533
534     mDefaultFontDescriptionCached = true;
535   }
536 }
537
538 // Validate
539
540 bool FontClient::Plugin::CacheHandler::FindValidatedFont(const FontDescription& fontDescription,
541                                                          FontDescriptionId&     fontDescriptionId)
542 {
543   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
544   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %zu\n", mValidatedFontCache.size());
545
546   fontDescriptionId = 0u;
547
548   // Fast cut if inputed family is empty.
549   if(DALI_UNLIKELY(fontDescription.family.empty()))
550   {
551     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description not found / fontDescription.family is empty!\n");
552     return false;
553   }
554
555   // Heuristic optimize code : Compare with latest found item.
556   if((fontDescription.width == mLatestFoundFontDescription.width) &&
557      (fontDescription.weight == mLatestFoundFontDescription.weight) &&
558      (fontDescription.slant == mLatestFoundFontDescription.slant) &&
559      (fontDescription.family == mLatestFoundFontDescription.family))
560   {
561     fontDescriptionId = mLatestFoundFontDescriptionId;
562
563     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description same as latest, id : %d\n", fontDescriptionId);
564     return true;
565   }
566
567   for(const auto& item : mValidatedFontCache)
568   {
569     if((fontDescription.width == item.fontDescription.width) &&
570        (fontDescription.weight == item.fontDescription.weight) &&
571        (fontDescription.slant == item.fontDescription.slant) &&
572        (fontDescription.family == item.fontDescription.family))
573     {
574       fontDescriptionId = item.index;
575
576       mLatestFoundFontDescription   = fontDescription;
577       mLatestFoundFontDescriptionId = fontDescriptionId;
578
579       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description found, id : %d\n", fontDescriptionId);
580       return true;
581     }
582   }
583
584   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description not found\n");
585   return false;
586 }
587
588 void FontClient::Plugin::CacheHandler::ValidateFont(const FontDescription& fontDescription,
589                                                     FontDescriptionId&     fontDescriptionId)
590 {
591   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
592   FONT_LOG_DESCRIPTION(fontDescription, "");
593
594   // Create a font pattern.
595   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
596
597   FontDescription description;
598
599   FcCharSet* characterSet = nullptr;
600   bool       matched      = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
601   FcPatternDestroy(fontFamilyPattern);
602
603   if(matched && (nullptr != characterSet))
604   {
605     // Add the path to the cache.
606     description.type = FontDescription::FACE_FONT;
607     mFontDescriptionCache.push_back(description);
608
609     // Set the index to the vector of paths to font file names.
610     fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
611
612     FONT_LOG_DESCRIPTION(description, "matched");
613     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fontDescriptionId : %d\n", fontDescriptionId);
614
615     // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
616     mCharacterSetCache.PushBack(characterSet);
617
618     if((fontDescription.family != description.family) ||
619        (fontDescription.width != description.width) ||
620        (fontDescription.weight != description.weight) ||
621        (fontDescription.slant != description.slant))
622     {
623       // Cache the given font's description if it's different than the matched.
624       CacheValidateFont(std::move(FontDescription(fontDescription)), fontDescriptionId);
625     }
626
627     // Cache the index and the matched font's description.
628     CacheValidateFont(std::move(description), fontDescriptionId);
629   }
630   else
631   {
632     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font validation failed for font [%s]\n", fontDescription.family.c_str());
633   }
634 }
635
636 void FontClient::Plugin::CacheHandler::CacheValidateFont(FontDescription&& fontDescription,
637                                                          FontDescriptionId validatedFontId)
638 {
639   mValidatedFontCache.emplace_back(std::move(FontDescriptionCacheItem(fontDescription, validatedFontId)));
640 }
641
642 // Fallback
643
644 bool FontClient::Plugin::CacheHandler::FindFallbackFontList(const FontDescription& fontDescription,
645                                                             FontList*&             fontList,
646                                                             CharacterSetList*&     characterSetList) const
647 {
648   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
649   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of fallback font lists in the cache : %zu\n", mFallbackCache.size());
650
651   fontList = nullptr;
652
653   for(const auto& item : mFallbackCache)
654   {
655     if(!fontDescription.family.empty() &&
656        (fontDescription.family == item.fontDescription.family) &&
657        (fontDescription.width == item.fontDescription.width) &&
658        (fontDescription.weight == item.fontDescription.weight) &&
659        (fontDescription.slant == item.fontDescription.slant))
660     {
661       fontList         = item.fallbackFonts;
662       characterSetList = item.characterSets;
663
664       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list found.\n");
665       return true;
666     }
667   }
668
669   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list not found.\n");
670   return false;
671 }
672
673 void FontClient::Plugin::CacheHandler::CacheFallbackFontList(FontDescription&&  fontDescription,
674                                                              FontList*&         fontList,
675                                                              CharacterSetList*& characterSetList)
676 {
677   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
678
679   fontList         = new FontList;
680   characterSetList = new CharacterSetList;
681
682   SetFontList(fontDescription, *fontList, *characterSetList);
683 #ifdef __APPLE__
684   FontDescription appleColorEmoji;
685   appleColorEmoji.family = "Apple Color Emoji";
686   appleColorEmoji.width  = fontDescription.width;
687   appleColorEmoji.weight = fontDescription.weight;
688   appleColorEmoji.slant  = fontDescription.slant;
689   FontList         emojiFontList;
690   CharacterSetList emojiCharSetList;
691   SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
692
693   std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
694   emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
695   *fontList         = std::move(emojiFontList);
696   *characterSetList = std::move(emojiCharSetList);
697 #endif
698
699   // Add the font-list to the cache.
700   mFallbackCache.push_back(std::move(CacheHandler::FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
701 }
702
703 // Font / FontFace
704
705 bool FontClient::Plugin::CacheHandler::FindFontByPath(const FontPath& path,
706                                                       PointSize26Dot6 requestedPointSize,
707                                                       FaceIndex       faceIndex,
708                                                       FontId&         fontId) const
709 {
710   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
711   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
712   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
713   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of fonts in the cache : %zu\n", mFontFaceCache.size());
714
715   fontId = 0u;
716   for(const auto& cacheItem : mFontFaceCache)
717   {
718     if(cacheItem.mRequestedPointSize == requestedPointSize &&
719        cacheItem.mFaceIndex == faceIndex &&
720        cacheItem.mPath == path)
721     {
722       fontId = cacheItem.mFontId + 1u;
723
724       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, id : %d\n", fontId);
725       return true;
726     }
727   }
728
729   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found\n");
730   return false;
731 }
732
733 bool FontClient::Plugin::CacheHandler::FindFont(FontDescriptionId fontDescriptionId,
734                                                 PointSize26Dot6   requestedPointSize,
735                                                 FontCacheIndex&   fontCacheIndex)
736 {
737   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
738   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "   fontDescriptionId : %d\n", fontDescriptionId);
739   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
740
741   fontCacheIndex = 0u;
742
743   const FontDescriptionSizeCacheKey key(fontDescriptionId, requestedPointSize);
744
745   // Heuristic optimize code : Compare with latest found item.
746   if(key == mLatestFoundCacheKey)
747   {
748     fontCacheIndex = mLatestFoundCacheIndex;
749
750     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font same as latest, index of font cache : %d\n", fontCacheIndex);
751     return true;
752   }
753
754   const auto& iter = mFontDescriptionSizeCache.find(key);
755   if(iter != mFontDescriptionSizeCache.cend())
756   {
757     fontCacheIndex = iter->second;
758
759     mLatestFoundCacheKey   = key;
760     mLatestFoundCacheIndex = fontCacheIndex;
761
762     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, index of font cache : %d\n", fontCacheIndex);
763     return true;
764   }
765
766   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found.\n");
767   return false;
768 }
769
770 void FontClient::Plugin::CacheHandler::CacheFontDescriptionSize(FontDescriptionId fontDescriptionId, PointSize26Dot6 requestedPointSize, FontCacheIndex fontCacheIndex)
771 {
772   mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
773 }
774
775 void FontClient::Plugin::CacheHandler::CacheFontPath(FT_Face ftFace, FontId fontId, PointSize26Dot6 requestedPointSize, const FontPath& path)
776 {
777   FontDescription description;
778   description.path   = path;
779   description.family = std::move(FontFamily(ftFace->family_name));
780   description.weight = FontWeight::NONE;
781   description.width  = FontWidth::NONE;
782   description.slant  = FontSlant::NONE;
783
784   // Note FreeType doesn't give too much info to build a proper font style.
785   if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
786   {
787     description.slant = FontSlant::ITALIC;
788   }
789   if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
790   {
791     description.weight = FontWeight::BOLD;
792   }
793
794   FontDescriptionId fontDescriptionId = 0u;
795   if(!FindValidatedFont(description, fontDescriptionId))
796   {
797     // TODO : Due to the FontClient pattern match process, we cannot pass dali-toolkit UTC.
798     // Can't we use ValidateFont here?
799     /*
800     // Use font config to validate the font's description.
801     ValidateFont(description, fontDescriptionId);
802
803     const FontCacheIndex fontCacheIndex          = mFontIdCache[fontId - 1u].index;
804     mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCharacterSetCache[fontDescriptionId - 1u]); // Increases the reference counter.
805
806     // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
807     mFontDescriptionSizeCache.emplace(CacheHandler::FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
808     */
809
810     FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
811
812     FcResult   result = FcResultMatch;
813     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
814
815     FcCharSet* characterSet = nullptr;
816     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
817
818     const FontCacheIndex fontCacheIndex          = mFontIdCache[fontId - 1u].index;
819     mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
820
821     // Destroys the created patterns.
822     FcPatternDestroy(match);
823     FcPatternDestroy(pattern);
824
825     // Add the path to the cache.
826     description.type = FontDescription::FACE_FONT;
827     mFontDescriptionCache.push_back(description);
828
829     // Set the index to the vector of paths to font file names.
830     fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
831
832     // Increase the reference counter and add the character set to the cache.
833     mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
834
835     // Cache the index and the font's description.
836     CacheValidateFont(std::move(description), fontDescriptionId);
837
838     // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
839     CacheFontDescriptionSize(fontDescriptionId, requestedPointSize, fontCacheIndex);
840   }
841 }
842
843 FontId FontClient::Plugin::CacheHandler::CacheFontFaceCacheItem(FontFaceCacheItem&& fontFaceCacheItem)
844 {
845   // Set the index to the font's id cache.
846   fontFaceCacheItem.mFontId = static_cast<FontId>(mFontIdCache.size());
847
848   // Create the font id item to cache.
849   FontIdCacheItem fontIdCacheItem;
850   fontIdCacheItem.type = FontDescription::FACE_FONT;
851
852   // Set the index to the FreeType font face cache.
853   fontIdCacheItem.index = static_cast<FontCacheIndex>(mFontFaceCache.size());
854
855   // Cache the items.
856   mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
857   mFontIdCache.emplace_back(std::move(fontIdCacheItem));
858
859   // Set the font id to be returned.
860   FontId fontId = static_cast<FontId>(mFontIdCache.size());
861
862   return fontId;
863 }
864
865 // Ellipsis
866
867 bool FontClient::Plugin::CacheHandler::FindEllipsis(PointSize26Dot6 requestedPointSize, EllipsisCacheIndex& ellipsisCacheIndex) const
868 {
869   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
870   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize);
871
872   ellipsisCacheIndex = 0u;
873
874   // First look into the cache if there is an ellipsis glyph for the requested point size.
875   for(const auto& item : mEllipsisCache)
876   {
877     if(item.requestedPointSize == requestedPointSize)
878     {
879       // Use the glyph in the cache.
880       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
881       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
882       ellipsisCacheIndex = item.index;
883       return true;
884     }
885   }
886   return false;
887 }
888
889 FontClient::Plugin::CacheHandler::EllipsisCacheIndex FontClient::Plugin::CacheHandler::CacheEllipsis(EllipsisItem&& ellipsisItem)
890 {
891   EllipsisCacheIndex ellipsisCacheIndex = static_cast<EllipsisCacheIndex>(mEllipsisCache.size());
892
893   mEllipsisCache.emplace_back(std::move(ellipsisItem));
894
895   return ellipsisCacheIndex;
896 }
897
898 // Bitmap font
899
900 bool FontClient::Plugin::CacheHandler::FindBitmapFont(const FontFamily& bitmapFontFamily, FontId& fontId) const
901 {
902   fontId = 0u;
903
904   for(const auto& item : mBitmapFontCache)
905   {
906     if(bitmapFontFamily == item.font.name)
907     {
908       fontId = item.id + 1u;
909       return true;
910     }
911   }
912
913   return false;
914 }
915
916 FontId FontClient::Plugin::CacheHandler::CacheBitmapFontCacheItem(BitmapFontCacheItem&& bitmapFontCacheItem)
917 {
918   // Set the index to the font's id cache.
919   bitmapFontCacheItem.id = static_cast<FontId>(mFontIdCache.size());
920
921   // Create the font id item to cache.
922   CacheHandler::FontIdCacheItem fontIdCacheItem;
923   fontIdCacheItem.type = FontDescription::BITMAP_FONT;
924
925   // Set the index to the Bitmap font face cache.
926   fontIdCacheItem.index = static_cast<FontCacheIndex>(mBitmapFontCache.size());
927
928   // Cache the items.
929   mBitmapFontCache.emplace_back(std::move(bitmapFontCacheItem));
930   mFontIdCache.emplace_back(std::move(fontIdCacheItem));
931
932   // Set the font id to be returned.
933   FontId fontId = static_cast<FontId>(mFontIdCache.size());
934
935   return fontId;
936 }
937
938 // Embedded
939
940 bool FontClient::Plugin::CacheHandler::FindEmbeddedPixelBufferId(const std::string& url, PixelBufferId& pixelBufferId) const
941 {
942   pixelBufferId = 0u;
943
944   for(const auto& cacheItem : mPixelBufferCache)
945   {
946     if(cacheItem.url == url)
947     {
948       // The url is in the pixel buffer cache.
949       pixelBufferId = cacheItem.id;
950       return true;
951     }
952   }
953
954   return false;
955 }
956
957 PixelBufferId FontClient::Plugin::CacheHandler::CacheEmbeddedPixelBuffer(const std::string& url)
958 {
959   PixelBufferId pixelBufferId = 0u;
960
961   // Load the image from the url.
962   Devel::PixelBuffer pixelBuffer = LoadImageFromFile(url);
963   if(pixelBuffer)
964   {
965     // Create the cache item.
966     PixelBufferCacheItem pixelBufferCacheItem;
967     pixelBufferCacheItem.pixelBuffer = pixelBuffer;
968     pixelBufferCacheItem.url         = url;
969     pixelBufferCacheItem.id          = static_cast<PixelBufferId>(mPixelBufferCache.size() + 1u);
970
971     // Store the cache item in the cache.
972     mPixelBufferCache.emplace_back(std::move(pixelBufferCacheItem));
973
974     // Set the pixel buffer id to be returned.
975     pixelBufferId = static_cast<PixelBufferId>(mPixelBufferCache.size());
976   }
977   return pixelBufferId;
978 }
979
980 bool FontClient::Plugin::CacheHandler::FindEmbeddedItem(PixelBufferId pixelBufferId, uint32_t width, uint32_t height, GlyphIndex& index) const
981 {
982   index = 0u;
983
984   for(const auto& cacheItem : mEmbeddedItemCache)
985   {
986     if((cacheItem.pixelBufferId == pixelBufferId) &&
987        (cacheItem.width == width) &&
988        (cacheItem.height == height))
989     {
990       index = cacheItem.index;
991       return true;
992     }
993   }
994
995   return false;
996 }
997
998 GlyphIndex FontClient::Plugin::CacheHandler::CacheEmbeddedItem(EmbeddedItem&& embeddedItem)
999 {
1000   embeddedItem.index = static_cast<GlyphIndex>(mEmbeddedItemCache.size() + 1u);
1001
1002   // Cache the embedded item.
1003   mEmbeddedItemCache.emplace_back(std::move(embeddedItem));
1004
1005   // Set the font id to be returned.
1006   GlyphIndex index = static_cast<GlyphIndex>(mEmbeddedItemCache.size());
1007
1008   return index;
1009 }
1010
1011 } // namespace Dali::TextAbstraction::Internal