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