Heuristic optimize on GetFontId
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-client-plugin-impl.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-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/font-list.h>
23
24 #include <dali/devel-api/adaptor-framework/image-loading.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/integration-api/platform-abstraction.h>
27 #include <dali/internal/adaptor/common/adaptor-impl.h>
28 #include <dali/internal/imaging/common/image-operations.h>
29 #include <dali/internal/text/text-abstraction/plugin/bitmap-font-cache-item.h>
30 #include <dali/internal/text/text-abstraction/plugin/embedded-item.h>
31 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
32 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
33 #include <dali/public-api/common/dali-vector.h>
34 #include <dali/public-api/common/vector-wrapper.h>
35
36 // EXTERNAL INCLUDES
37 #include <fontconfig/fontconfig.h>
38 #include <algorithm>
39 #include <iterator>
40
41 #if defined(DEBUG_ENABLED)
42
43 // Note, to turn on trace and verbose logging, use "export LOG_FONT_CLIENT=3,true"
44 // Or re-define the following filter using Verbose,true instead of NoLogging,false,
45 // Or, add DALI_LOG_FILTER_ENABLE_TRACE(gFontClientLogFilter) in the code below.
46
47 Dali::Integration::Log::Filter* gFontClientLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
48
49 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)                                                                            \
50   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix "  description; family : [%s]\n", fontDescription.family.c_str()); \
51   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose,                                                                            \
52                 "                 path : [%s]\n"                                                                                 \
53                 "                width : [%s]\n"                                                                                 \
54                 "               weight : [%s]\n"                                                                                 \
55                 "                slant : [%s]\n\n",                                                                              \
56                 fontDescription.path.c_str(),                                                                                    \
57                 FontWidth::Name[fontDescription.width],                                                                          \
58                 FontWeight::Name[fontDescription.weight],                                                                        \
59                 FontSlant::Name[fontDescription.slant])
60
61 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
62   DALI_LOG_INFO(gFontClientLogFilter, Debug::General,               \
63                 "           character : %p\n"                       \
64                 "  requestedPointSize : %d\n"                       \
65                 "         preferColor : %s\n",                      \
66                 charcode,                                           \
67                 requestedPointSize,                                 \
68                 (preferColor ? "true" : "false"))
69
70 #else
71
72 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
73 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
74
75 #endif
76
77 namespace
78 {
79 /**
80  * Conversion from Fractional26.6 to float
81  */
82 const float FROM_266        = 1.0f / 64.0f;
83 const float POINTS_PER_INCH = 72.f;
84
85 const std::string DEFAULT_FONT_FAMILY_NAME("Tizen");
86
87 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
88
89 } // namespace
90
91 using Dali::Vector;
92 using namespace std;
93
94 namespace Dali::TextAbstraction::Internal
95 {
96 /**
97  * @brief Free the resources allocated by the FcCharSet objects.
98  *
99  * @param[in] characterSets The vector of character sets.
100  */
101 void DestroyCharacterSets(CharacterSetList& characterSets)
102 {
103   for(auto& item : characterSets)
104   {
105     if(item)
106     {
107       FcCharSetDestroy(item);
108     }
109   }
110 }
111
112 /**
113  * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
114  *
115  * @param[in/out] ftFace Face type object.
116  * @param[in] horizontalDpi The horizontal dpi.
117  * @param[in] verticalDpi The vertical dpi.
118  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
119  * @param[in] requestedPointSize The requested point-size.
120  * @return whether the  ftFace's block can fit into atlas
121  */
122 bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
123 {
124   bool isFit = false;
125
126   error = FT_Set_Char_Size(ftFace,
127                            0,
128                            requestedPointSize,
129                            horizontalDpi,
130                            verticalDpi);
131
132   if(error == FT_Err_Ok)
133   {
134     //Check width and height of block for requestedPointSize
135     //If the width or height is greater than the maximum-size then decrement by one unit of point-size.
136     if(static_cast<float>(ftFace->size->metrics.height) * FROM_266 <= maxSizeFitInAtlas.height && (static_cast<float>(ftFace->size->metrics.ascender) - static_cast<float>(ftFace->size->metrics.descender)) * FROM_266 <= maxSizeFitInAtlas.width)
137     {
138       isFit = true;
139     }
140   }
141
142   return isFit;
143 }
144
145 /**
146  * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
147  *
148  * @param[in/out] ftFace Face type object.
149  * @param[in] horizontalDpi The horizontal dpi.
150  * @param[in] verticalDpi The vertical dpi.
151  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
152  * @param[in/out] requestedPointSize The requested point-size.
153  * @return FreeType error code. 0 means success when requesting the nominal size (in points).
154  */
155 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
156 {
157   //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
158   const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
159   bool            canFitInAtlas;
160   int             error; // FreeType error code.
161
162   canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
163   if(FT_Err_Ok != error)
164   {
165     return error;
166   }
167
168   if(!canFitInAtlas)
169   {
170     //Exponential search
171     uint32_t exponentialDecrement = 1;
172
173     while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit * exponentialDecrement)
174     {
175       requestedPointSize -= (pointSizePerOneUnit * exponentialDecrement);
176       canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
177       if(FT_Err_Ok != error)
178       {
179         return error;
180       }
181
182       exponentialDecrement *= 2;
183     }
184
185     //Binary search
186     uint32_t minPointSize;
187     uint32_t maxPointSize;
188
189     if(canFitInAtlas)
190     {
191       exponentialDecrement /= 2;
192       minPointSize = requestedPointSize;
193       maxPointSize = requestedPointSize + (pointSizePerOneUnit * exponentialDecrement);
194     }
195     else
196     {
197       minPointSize = 0;
198       maxPointSize = requestedPointSize;
199     }
200
201     while(minPointSize < maxPointSize)
202     {
203       requestedPointSize = ((maxPointSize / pointSizePerOneUnit - minPointSize / pointSizePerOneUnit) / 2) * pointSizePerOneUnit + minPointSize;
204       canFitInAtlas      = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
205       if(FT_Err_Ok != error)
206       {
207         return error;
208       }
209
210       if(canFitInAtlas)
211       {
212         if(minPointSize == requestedPointSize)
213         {
214           //Found targeted point-size
215           return error;
216         }
217
218         minPointSize = requestedPointSize;
219       }
220       else
221       {
222         maxPointSize = requestedPointSize;
223       }
224     }
225   }
226
227   return error;
228 }
229
230 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
231 : fontDescription{std::move(font)},
232   fallbackFonts{fallbackFonts},
233   characterSets{characterSets}
234 {
235 }
236
237 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
238                                                                        FontDescriptionId      index)
239 : fontDescription{fontDescription},
240   index{index}
241 {
242 }
243
244 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
245                                                                        FontDescriptionId index)
246 : fontDescription{std::move(fontDescription)},
247   index{index}
248 {
249 }
250
251 FontClient::Plugin::FontDescriptionSizeCacheKey::FontDescriptionSizeCacheKey(FontDescriptionId fontDescriptionId,
252                                                                              PointSize26Dot6   requestedPointSize)
253 : fontDescriptionId(fontDescriptionId),
254   requestedPointSize(requestedPointSize)
255 {
256 }
257
258 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
259                            unsigned int verticalDpi)
260 : mFreeTypeLibrary(nullptr),
261   mDpiHorizontal(horizontalDpi),
262   mDpiVertical(verticalDpi),
263   mDefaultFontDescription(),
264   mSystemFonts(),
265   mDefaultFonts(),
266   mFontIdCache(),
267   mFontFaceCache(),
268   mValidatedFontCache(),
269   mFontDescriptionCache(),
270   mCharacterSetCache(),
271   mFontDescriptionSizeCache(),
272   mVectorFontCache(nullptr),
273   mEllipsisCache(),
274   mEmbeddedItemCache(),
275   mLatestFoundFontDescription(),
276   mLatestFoundCacheKey(0, 0),
277   mDefaultFontDescriptionCached(false),
278   mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
279   mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS)
280
281 {
282   int error = FT_Init_FreeType(&mFreeTypeLibrary);
283   if(FT_Err_Ok != error)
284   {
285     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Init error: %d\n", error);
286   }
287
288 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
289   mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
290 #endif
291 }
292
293 FontClient::Plugin::~Plugin()
294 {
295   ClearFallbackCache(mFallbackCache);
296
297   // Free the resources allocated by the FcCharSet objects.
298   DestroyCharacterSets(mDefaultFontCharacterSets);
299   DestroyCharacterSets(mCharacterSetCache);
300   ClearCharacterSetFromFontFaceCache();
301
302   // Clear FontFaceCache here. Due to we sould deallocate FT_Faces before done freetype library
303   mFontFaceCache.clear();
304
305 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
306   delete mVectorFontCache;
307 #endif
308   FT_Done_FreeType(mFreeTypeLibrary);
309 }
310
311 void FontClient::Plugin::ClearCache()
312 {
313   mDefaultFontDescription = FontDescription();
314
315   mSystemFonts.clear();
316   mDefaultFonts.clear();
317
318   DestroyCharacterSets(mDefaultFontCharacterSets);
319   mDefaultFontCharacterSets.Clear();
320
321   ClearFallbackCache(mFallbackCache);
322   mFallbackCache.clear();
323
324   mFontIdCache.Clear();
325
326   ClearCharacterSetFromFontFaceCache();
327   mFontFaceCache.clear();
328
329   mValidatedFontCache.clear();
330   mFontDescriptionCache.clear();
331
332   DestroyCharacterSets(mCharacterSetCache);
333   mCharacterSetCache.Clear();
334
335   mFontDescriptionSizeCache.clear();
336   mFontDescriptionSizeCache.rehash(0); // Note : unordered_map.clear() didn't deallocate memory
337
338   mEllipsisCache.Clear();
339   mPixelBufferCache.clear();
340   mEmbeddedItemCache.Clear();
341   mBitmapFontCache.clear();
342
343   mLatestFoundFontDescription.family.clear();
344   mLatestFoundCacheKey = FontDescriptionSizeCacheKey(0, 0);
345
346   mDefaultFontDescriptionCached = false;
347 }
348
349 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
350                                 unsigned int verticalDpi)
351 {
352   mDpiHorizontal = horizontalDpi;
353   mDpiVertical   = verticalDpi;
354 }
355
356 void FontClient::Plugin::ResetSystemDefaults()
357 {
358   mDefaultFontDescriptionCached = false;
359 }
360
361 void FontClient::Plugin::SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
362 {
363   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
364   FONT_LOG_DESCRIPTION(fontDescription, "");
365   fontList.clear();
366
367   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
368
369   FcResult result = FcResultMatch;
370
371   // Match the pattern.
372   FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
373                                   fontFamilyPattern,
374                                   false /* don't trim */,
375                                   nullptr,
376                                   &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
377
378   if(nullptr != fontSet)
379   {
380     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of fonts found : [%d]\n", fontSet->nfont);
381     // Reserve some space to avoid reallocations.
382     fontList.reserve(fontSet->nfont);
383
384     for(int i = 0u; i < fontSet->nfont; ++i)
385     {
386       FcPattern* fontPattern = fontSet->fonts[i];
387
388       FontPath path;
389
390       // Skip fonts with no path
391       if(GetFcString(fontPattern, FC_FILE, path))
392       {
393         // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
394         FcCharSet* characterSet = nullptr;
395         FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
396
397         // Increase the reference counter of the character set.
398         characterSetList.PushBack(FcCharSetCopy(characterSet));
399
400         fontList.push_back(FontDescription());
401         FontDescription& newFontDescription = fontList.back();
402
403         newFontDescription.path = std::move(path);
404
405         int width  = 0;
406         int weight = 0;
407         int slant  = 0;
408         GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
409         GetFcInt(fontPattern, FC_WIDTH, width);
410         GetFcInt(fontPattern, FC_WEIGHT, weight);
411         GetFcInt(fontPattern, FC_SLANT, slant);
412         newFontDescription.width  = IntToWidthType(width);
413         newFontDescription.weight = IntToWeightType(weight);
414         newFontDescription.slant  = IntToSlantType(slant);
415
416         FONT_LOG_DESCRIPTION(newFontDescription, "");
417       }
418     }
419
420     // Destroys the font set created by FcFontSort.
421     FcFontSetDestroy(fontSet);
422   }
423   else
424   {
425     DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  No fonts found.\n");
426   }
427
428   // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
429   FcPatternDestroy(fontFamilyPattern);
430 }
431
432 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts)
433 {
434   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
435
436   if(mDefaultFonts.empty())
437   {
438     FontDescription fontDescription;
439     fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font
440     fontDescription.width  = DefaultFontWidth();
441     fontDescription.weight = DefaultFontWeight();
442     fontDescription.slant  = DefaultFontSlant();
443     SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
444   }
445
446   defaultFonts = mDefaultFonts;
447
448   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of default fonts : [%d]\n", mDefaultFonts.size());
449 }
450
451 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
452 {
453   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
454
455   if(!mDefaultFontDescriptionCached)
456   {
457     // Clear any font config stored info in the caches.
458
459     // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
460     DestroyCharacterSets(mDefaultFontCharacterSets);
461     DestroyCharacterSets(mCharacterSetCache);
462     mDefaultFontCharacterSets.Clear();
463     mCharacterSetCache.Clear();
464
465     for(auto& item : mFallbackCache)
466     {
467       // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
468       DestroyCharacterSets(*item.characterSets);
469
470       delete item.characterSets;
471       item.characterSets = nullptr;
472     }
473
474     // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
475     ClearCharacterSetFromFontFaceCache();
476
477     // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
478     FcInitReinitialize();
479
480     FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
481
482     if(nullptr != matchPattern)
483     {
484       FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
485       FcDefaultSubstitute(matchPattern);
486
487       FcCharSet* characterSet = nullptr;
488       bool       matched      = MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
489
490       // Caching the default font description
491       if(matched)
492       {
493         // Copy default font description info.
494         // Due to the type changed, we need to make some temperal font description.
495         FontDescription tempFontDescription = mDefaultFontDescription;
496
497         // Add the path to the cache.
498         tempFontDescription.type = FontDescription::FACE_FONT;
499         mFontDescriptionCache.push_back(tempFontDescription);
500
501         // Set the index to the vector of paths to font file names.
502         const FontDescriptionId fontDescriptionId = mFontDescriptionCache.size();
503
504         FONT_LOG_DESCRIPTION(tempFontDescription, "default platform font");
505         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  default font fontDescriptionId : %d\n", fontDescriptionId);
506
507         // Cache the index and the matched font's description.
508         FontDescriptionCacheItem item(tempFontDescription,
509                                       fontDescriptionId);
510
511         mValidatedFontCache.push_back(std::move(item));
512       }
513       else
514       {
515         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  default font validation failed for font [%s]\n", mDefaultFontDescription.family.c_str());
516       }
517
518       // Decrease the reference counter of the character set as it's not stored.
519       // Note. the cached default font description will increase reference counter by
520       // mFontDescriptionCache. So we can decrease reference counter here.
521       FcCharSetDestroy(characterSet);
522
523       // Destroys the pattern created.
524       FcPatternDestroy(matchPattern);
525     }
526
527     // Create again the character sets as they are not valid after FcInitReinitialize()
528
529     for(const auto& description : mDefaultFonts)
530     {
531       mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
532     }
533
534     for(const auto& description : mFontDescriptionCache)
535     {
536       mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
537     }
538
539     for(auto& item : mFallbackCache)
540     {
541       if(nullptr != item.fallbackFonts)
542       {
543         if(nullptr == item.characterSets)
544         {
545           item.characterSets = new CharacterSetList;
546         }
547
548         for(const auto& description : *(item.fallbackFonts))
549         {
550           item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
551         }
552       }
553     }
554
555     mDefaultFontDescriptionCached = true;
556   }
557
558   fontDescription.path   = mDefaultFontDescription.path;
559   fontDescription.family = mDefaultFontDescription.family;
560   fontDescription.width  = mDefaultFontDescription.width;
561   fontDescription.weight = mDefaultFontDescription.weight;
562   fontDescription.slant  = mDefaultFontDescription.slant;
563
564   FONT_LOG_DESCRIPTION(fontDescription, "");
565 }
566
567 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts)
568 {
569   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
570
571   if(mSystemFonts.empty())
572   {
573     InitSystemFonts();
574   }
575
576   systemFonts = mSystemFonts;
577   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : [%d]\n", mSystemFonts.size());
578 }
579
580 void FontClient::Plugin::GetDescription(FontId           id,
581                                         FontDescription& fontDescription) const
582 {
583   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
584   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
585   const FontId index = id - 1u;
586
587   if((id > 0u) && (index < mFontIdCache.Count()))
588   {
589     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
590     switch(fontIdCacheItem.type)
591     {
592       case FontDescription::FACE_FONT:
593       {
594         for(const auto& item : mFontDescriptionSizeCache)
595         {
596           if(item.second == fontIdCacheItem.index)
597           {
598             fontDescription = *(mFontDescriptionCache.begin() + item.first.fontDescriptionId - 1u);
599
600             FONT_LOG_DESCRIPTION(fontDescription, "");
601             return;
602           }
603         }
604         break;
605       }
606       case FontDescription::BITMAP_FONT:
607       {
608         fontDescription.type   = FontDescription::BITMAP_FONT;
609         fontDescription.family = mBitmapFontCache[fontIdCacheItem.index].font.name;
610         break;
611       }
612       default:
613       {
614         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Invalid type of font\n");
615         fontDescription.type = FontDescription::INVALID;
616         fontDescription.family.clear();
617       }
618     }
619   }
620
621   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  No description found for the font ID %d\n", id);
622 }
623
624 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id)
625 {
626   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
627   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
628
629   PointSize26Dot6               pointSize     = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
630   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(id);
631   if(fontCacheItem != nullptr)
632   {
633     pointSize = fontCacheItem->GetPointSize();
634   }
635   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  point size : %d\n", pointSize);
636
637   return pointSize;
638 }
639
640 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character)
641 {
642   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
643   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "    font id : %d\n", fontId);
644   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  character : %p\n", character);
645
646   bool isSupported   = false;
647   auto fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
648   if(fontCacheItem != nullptr)
649   {
650     isSupported = fontCacheItem->IsCharacterSupported(character); // May cache
651   }
652
653   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  is supported : %s\n", (isSupported ? "true" : "false"));
654   return isSupported;
655 }
656
657 const FontCacheItemInterface* FontClient::Plugin::GetCachedFontItem(FontId id) const
658 {
659   const FontCacheIndex index = id - 1u;
660   if((id > 0u) && (index < mFontIdCache.Count()))
661   {
662     const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index];
663     switch(fontIdCacheItem.type)
664     {
665       case FontDescription::FACE_FONT:
666       {
667         return &mFontFaceCache[fontIdCacheItem.index];
668       }
669       case FontDescription::BITMAP_FONT:
670       {
671         return &mBitmapFontCache[fontIdCacheItem.index];
672       }
673       default:
674       {
675         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Invalid type of font\n");
676       }
677     }
678   }
679   return nullptr;
680 }
681
682 FontId FontClient::Plugin::FindFontForCharacter(const FontList&         fontList,
683                                                 const CharacterSetList& characterSetList,
684                                                 Character               character,
685                                                 PointSize26Dot6         requestedPointSize,
686                                                 bool                    preferColor)
687 {
688   DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
689   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
690   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "           character : %p\n", character);
691   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
692   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "         preferColor : %s\n", (preferColor ? "true" : "false"));
693
694   FontId fontId     = 0u;
695   bool   foundColor = false;
696
697   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of fonts : %d\n", fontList.size());
698
699   // Traverse the list of fonts.
700   // Check for each font if supports the character.
701   for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
702   {
703     const FontDescription& description  = fontList[index];
704     const FcCharSet* const characterSet = characterSetList[index];
705
706     FONT_LOG_DESCRIPTION(description, "");
707
708     bool foundInRanges = false;
709     if(nullptr != characterSet)
710     {
711       foundInRanges = FcCharSetHasChar(characterSet, character);
712     }
713
714     if(foundInRanges)
715     {
716       fontId = GetFontId(description, requestedPointSize, 0u);
717
718       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "     font id : %d\n", fontId);
719
720       if(preferColor)
721       {
722         if((fontId > 0) &&
723            (fontId - 1u < mFontIdCache.Count()))
724         {
725           const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].index];
726
727           foundColor = item.mHasColorTables;
728         }
729
730         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  foundColor : %s\n", (foundColor ? "true" : "false"));
731       }
732
733       // Keep going unless we prefer a different (color) font.
734       if(!preferColor || foundColor)
735       {
736         break;
737       }
738     }
739   }
740
741   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
742   return fontId;
743 }
744
745 FontId FontClient::Plugin::FindDefaultFont(Character       charcode,
746                                            PointSize26Dot6 requestedPointSize,
747                                            bool            preferColor)
748 {
749   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
750   FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
751
752   FontId fontId(0);
753
754   // Create the list of default fonts if it has not been created.
755   if(mDefaultFonts.empty())
756   {
757     FontDescription fontDescription;
758     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
759     fontDescription.width  = DefaultFontWidth();
760     fontDescription.weight = DefaultFontWeight();
761     fontDescription.slant  = DefaultFontSlant();
762
763     SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
764   }
765   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of default fonts : %d\n", mDefaultFonts.size());
766
767   // Traverse the list of default fonts.
768   // Check for each default font if supports the character.
769   fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
770
771   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
772   return fontId;
773 }
774
775 FontId FontClient::Plugin::FindFallbackFont(Character              charcode,
776                                             const FontDescription& preferredFontDescription,
777                                             PointSize26Dot6        requestedPointSize,
778                                             bool                   preferColor)
779 {
780   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
781   FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
782
783   // The font id to be returned.
784   FontId fontId = 0u;
785
786   FontDescription fontDescription;
787
788   // Fill the font description with the preferred font description and complete with the defaults.
789   fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
790   fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? DefaultFontWeight() : preferredFontDescription.weight);
791   fontDescription.width  = ((FontWidth::NONE == preferredFontDescription.width) ? DefaultFontWidth() : preferredFontDescription.width);
792   fontDescription.slant  = ((FontSlant::NONE == preferredFontDescription.slant) ? DefaultFontSlant() : preferredFontDescription.slant);
793
794   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  preferredFontDescription --> fontDescription\n");
795   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
796   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
797   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
798   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
799
800   // Check first if the font's description has been queried before.
801   FontList*         fontList         = nullptr;
802   CharacterSetList* characterSetList = nullptr;
803
804   if(!FindFallbackFontList(fontDescription, fontList, characterSetList))
805   {
806     fontList         = new FontList;
807     characterSetList = new CharacterSetList;
808
809     SetFontList(fontDescription, *fontList, *characterSetList);
810 #ifdef __APPLE__
811     FontDescription appleColorEmoji;
812     appleColorEmoji.family = "Apple Color Emoji";
813     appleColorEmoji.width  = fontDescription.width;
814     appleColorEmoji.weight = fontDescription.weight;
815     appleColorEmoji.slant  = fontDescription.slant;
816     FontList         emojiFontList;
817     CharacterSetList emojiCharSetList;
818     SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
819
820     std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
821     emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
822     *fontList         = std::move(emojiFontList);
823     *characterSetList = std::move(emojiCharSetList);
824 #endif
825
826     // Add the font-list to the cache.
827     mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
828   }
829
830   if(fontList && characterSetList)
831   {
832     fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
833   }
834
835   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
836   return fontId;
837 }
838
839 FontId FontClient::Plugin::GetFontId(const FontPath& path,
840                                      PointSize26Dot6 requestedPointSize,
841                                      FaceIndex       faceIndex,
842                                      bool            cacheDescription)
843 {
844   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
845   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
846   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
847
848   FontId id = 0u;
849
850   if(nullptr != mFreeTypeLibrary)
851   {
852     FontId foundId = 0u;
853     if(FindFont(path, requestedPointSize, faceIndex, foundId))
854     {
855       id = foundId;
856     }
857     else
858     {
859       id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
860     }
861   }
862
863   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
864   return id;
865 }
866
867 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
868                                      PointSize26Dot6        requestedPointSize,
869                                      FaceIndex              faceIndex)
870 {
871   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
872   FONT_LOG_DESCRIPTION(fontDescription, "");
873
874   // Special case when font Description don't have family information.
875   // In this case, we just use default description family and path.
876   const FontDescription& realFontDescription = fontDescription.family.empty() ? FontDescription(mDefaultFontDescription.path,
877                                                                                                 mDefaultFontDescription.family,
878                                                                                                 fontDescription.width,
879                                                                                                 fontDescription.weight,
880                                                                                                 fontDescription.slant,
881                                                                                                 fontDescription.type)
882                                                                               : fontDescription;
883
884   FONT_LOG_DESCRIPTION(realFontDescription, "");
885   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "   requestedPointSize : %d\n", requestedPointSize);
886
887   // This method uses three vectors which caches:
888   // * The bitmap font cache
889   // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
890   // * The path to font file names.
891   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
892
893   // 1) Checks if the font description matches with a previously loaded bitmap font.
894   //    Returns if a font is found.
895   // 2) Checks in the cache if the font's description has been validated before.
896   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
897   //    retrieves using font config a path to a font file name which matches with the
898   //    font's description. The path is stored in the cache.
899   //
900   // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
901   //    font file names' exists. If exists, it gets the font id. If it doesn't it calls
902   //    the GetFontId() method with the path to the font file name and the point size to
903   //    get the font id.
904
905   // The font id to be returned.
906   FontId fontId = 0u;
907
908   // Check first if the font description matches with a previously loaded bitmap font.
909   if(FindBitmapFont(realFontDescription.family, fontId))
910   {
911     return fontId;
912   }
913
914   // Check if the font's description have been validated before.
915   FontDescriptionId fontDescriptionId = 0u;
916
917   if(!FindValidatedFont(realFontDescription,
918                         fontDescriptionId))
919   {
920     // Use font config to validate the font's description.
921     ValidateFont(realFontDescription,
922                  fontDescriptionId);
923   }
924
925   FontCacheIndex fontCacheIndex = 0u;
926   // Check if exists a pair 'fontDescriptionId, requestedPointSize' in the cache.
927   if(!FindFont(fontDescriptionId, requestedPointSize, fontCacheIndex))
928   {
929     // Retrieve the font file name path.
930     const FontDescription& description = *(mFontDescriptionCache.begin() + fontDescriptionId - 1u);
931
932     // Retrieve the font id. Do not cache the description as it has been already cached.
933     fontId = GetFontId(description.path,
934                        requestedPointSize,
935                        faceIndex,
936                        false);
937
938     fontCacheIndex                               = mFontIdCache[fontId - 1u].index;
939     mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCharacterSetCache[fontDescriptionId - 1u]);
940
941     // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
942     mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
943   }
944   else
945   {
946     fontId = mFontFaceCache[fontCacheIndex].mFontId + 1u;
947   }
948
949   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
950   return fontId;
951 }
952
953 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont)
954 {
955   for(const auto& item : mBitmapFontCache)
956   {
957     if(bitmapFont.name == item.font.name)
958     {
959       return item.id + 1u;
960     }
961   }
962
963   BitmapFontCacheItem bitmapFontCacheItem(bitmapFont, mFontIdCache.Count());
964
965   FontIdCacheItem fontIdCacheItem;
966   fontIdCacheItem.type  = FontDescription::BITMAP_FONT;
967   fontIdCacheItem.index = mBitmapFontCache.size();
968
969   mBitmapFontCache.push_back(std::move(bitmapFontCacheItem));
970   mFontIdCache.PushBack(fontIdCacheItem);
971
972   return bitmapFontCacheItem.id + 1u;
973 }
974
975 void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription,
976                                       FontDescriptionId&     fontDescriptionId)
977 {
978   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
979   FONT_LOG_DESCRIPTION(fontDescription, "");
980
981   // Create a font pattern.
982   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
983
984   FontDescription description;
985
986   FcCharSet* characterSet = nullptr;
987   bool       matched      = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
988   FcPatternDestroy(fontFamilyPattern);
989
990   if(matched && (nullptr != characterSet))
991   {
992     // Add the path to the cache.
993     description.type = FontDescription::FACE_FONT;
994     mFontDescriptionCache.push_back(description);
995
996     // Set the index to the vector of paths to font file names.
997     fontDescriptionId = mFontDescriptionCache.size();
998
999     FONT_LOG_DESCRIPTION(description, "matched");
1000     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fontDescriptionId : %d\n", fontDescriptionId);
1001
1002     // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
1003     mCharacterSetCache.PushBack(characterSet);
1004
1005     // Cache the index and the matched font's description.
1006     FontDescriptionCacheItem item(description,
1007                                   fontDescriptionId);
1008
1009     mValidatedFontCache.push_back(std::move(item));
1010
1011     if((fontDescription.family != description.family) ||
1012        (fontDescription.width != description.width) ||
1013        (fontDescription.weight != description.weight) ||
1014        (fontDescription.slant != description.slant))
1015     {
1016       // Cache the given font's description if it's different than the matched.
1017       FontDescriptionCacheItem item(fontDescription,
1018                                     fontDescriptionId);
1019
1020       mValidatedFontCache.push_back(std::move(item));
1021     }
1022   }
1023   else
1024   {
1025     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font validation failed for font [%s]\n", fontDescription.family.c_str());
1026   }
1027 }
1028
1029 void FontClient::Plugin::GetFontMetrics(FontId       fontId,
1030                                         FontMetrics& metrics)
1031 {
1032   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1033   if(fontCacheItem != nullptr)
1034   {
1035     fontCacheItem->GetFontMetrics(metrics, mDpiVertical);
1036   }
1037 }
1038
1039 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
1040                                              Character charcode)
1041 {
1042   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1043   if(fontCacheItem != nullptr)
1044   {
1045     return fontCacheItem->GetGlyphIndex(charcode);
1046   }
1047
1048   return 0u;
1049 }
1050
1051 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
1052                                              Character charcode,
1053                                              Character variantSelector)
1054 {
1055   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1056   if(fontCacheItem != nullptr)
1057   {
1058     return fontCacheItem->GetGlyphIndex(charcode, variantSelector);
1059   }
1060
1061   return 0u;
1062 }
1063
1064 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
1065                                          uint32_t   size,
1066                                          GlyphType  type,
1067                                          bool       horizontal)
1068 {
1069   if(VECTOR_GLYPH == type)
1070   {
1071     return GetVectorMetrics(array, size, horizontal);
1072   }
1073
1074   return GetBitmapMetrics(array, size, horizontal);
1075 }
1076
1077 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
1078                                           uint32_t   size,
1079                                           bool       horizontal)
1080 {
1081   bool success(false);
1082
1083   for(unsigned int i = 0; i < size; ++i)
1084   {
1085     GlyphInfo& glyph = array[i];
1086
1087     const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(glyph.fontId);
1088     if(fontCacheItem != nullptr)
1089     {
1090       success = fontCacheItem->GetGlyphMetrics(glyph, mDpiVertical, horizontal);
1091     }
1092     // Check if it's an embedded image.
1093     else if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count()))
1094     {
1095       mEmbeddedItemCache[glyph.index - 1u].GetGlyphMetrics(glyph);
1096       success = true;
1097     }
1098   }
1099
1100   return success;
1101 }
1102
1103 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
1104                                           uint32_t   size,
1105                                           bool       horizontal)
1106 {
1107 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1108   bool success(true);
1109
1110   for(unsigned int i = 0u; i < size; ++i)
1111   {
1112     FontId fontId = array[i].fontId;
1113
1114     if((fontId > 0u) &&
1115        (fontId - 1u) < mFontIdCache.Count())
1116     {
1117       FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].index];
1118
1119       if(!font.mVectorFontId)
1120       {
1121         font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1122       }
1123
1124       mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
1125
1126       // Vector metrics are in EMs, convert to pixels
1127       const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
1128       array[i].width *= scale;
1129       array[i].height *= scale;
1130       array[i].xBearing *= scale;
1131       array[i].yBearing *= scale;
1132       array[i].advance *= scale;
1133     }
1134     else
1135     {
1136       success = false;
1137     }
1138   }
1139
1140   return success;
1141 #else
1142   return false;
1143 #endif
1144 }
1145
1146 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
1147 {
1148   data.isColorBitmap                          = false;
1149   data.isColorEmoji                           = false;
1150   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1151   if(fontCacheItem != nullptr)
1152   {
1153     fontCacheItem->CreateBitmap(glyphIndex, data, outlineWidth, isItalicRequired, isBoldRequired);
1154   }
1155   else if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count()))
1156   {
1157     // It's an embedded item.
1158     mEmbeddedItemCache[glyphIndex - 1u].CreateBitmap(mPixelBufferCache, data);
1159   }
1160 }
1161
1162 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
1163 {
1164   TextAbstraction::FontClient::GlyphBufferData data;
1165
1166   CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
1167
1168   return PixelData::New(data.buffer,
1169                         data.width * data.height * Pixel::GetBytesPerPixel(data.format),
1170                         data.width,
1171                         data.height,
1172                         data.format,
1173                         PixelData::FREE);
1174 }
1175
1176 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
1177 {
1178   blob       = nullptr;
1179   blobLength = 0;
1180
1181 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1182   if((fontId > 0u) &&
1183      (fontId - 1u < mFontIdCache.Count()))
1184   {
1185     const FontCacheIndex fontFaceId = mFontIdCache[fontId - 1u].index;
1186     FontFaceCacheItem&   font       = mFontFaceCache[fontFaceId];
1187
1188     if(!font.mVectorFontId)
1189     {
1190       font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
1191     }
1192
1193     mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
1194   }
1195 #endif
1196 }
1197
1198 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
1199 {
1200   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1201   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize);
1202
1203   // First look into the cache if there is an ellipsis glyph for the requested point size.
1204   for(const auto& item : mEllipsisCache)
1205   {
1206     if(item.requestedPointSize == requestedPointSize)
1207     {
1208       // Use the glyph in the cache.
1209       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
1210       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
1211       return item.glyph;
1212     }
1213   }
1214
1215   // No glyph has been found. Create one.
1216   mEllipsisCache.PushBack(EllipsisItem());
1217   EllipsisItem& item = *(mEllipsisCache.End() - 1u);
1218
1219   item.requestedPointSize = requestedPointSize;
1220
1221   // Find a font for the ellipsis glyph.
1222   item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
1223                                       requestedPointSize,
1224                                       false);
1225
1226   // Set the character index to access the glyph inside the font.
1227   item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].index].mFreeTypeFace,
1228                                        ELLIPSIS_CHARACTER);
1229
1230   GetBitmapMetrics(&item.glyph, 1u, true);
1231
1232   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
1233   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
1234   return item.glyph;
1235 }
1236
1237 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
1238 {
1239   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1240   return fontCacheItem && fontCacheItem->IsColorGlyph(glyphIndex);
1241 }
1242
1243 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId)
1244 {
1245   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1246   if(fontCacheItem != nullptr)
1247   {
1248     return fontCacheItem->GetTypeface();
1249   }
1250   return nullptr;
1251 }
1252
1253 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId)
1254 {
1255   const FontId index = fontId - 1u;
1256   if((fontId > 0u) &&
1257      (index < mFontIdCache.Count()))
1258   {
1259     return mFontIdCache[index].type;
1260   }
1261   return FontDescription::INVALID;
1262 }
1263
1264 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
1265 {
1266   // nullptr as first parameter means the current configuration is used.
1267   return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
1268 }
1269
1270 HarfBuzzFontHandle FontClient::Plugin::GetHarfBuzzFont(FontId fontId)
1271 {
1272   FontCacheItemInterface* fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
1273   if(fontCacheItem != nullptr)
1274   {
1275     return fontCacheItem->GetHarfBuzzFont(mDpiHorizontal, mDpiVertical); // May cache
1276   }
1277   return nullptr;
1278 }
1279
1280 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
1281 {
1282   EmbeddedItem embeddedItem;
1283
1284   embeddedItem.pixelBufferId = 0u;
1285   embeddedItem.width         = description.width;
1286   embeddedItem.height        = description.height;
1287
1288   pixelFormat = Pixel::A8;
1289
1290   if(!description.url.empty())
1291   {
1292     // Check if the url is in the cache.
1293     PixelBufferId index = 0u;
1294
1295     for(const auto& cacheItem : mPixelBufferCache)
1296     {
1297       ++index;
1298       if(cacheItem.url == description.url)
1299       {
1300         // The url is in the pixel buffer cache.
1301         // Set the index +1 to the vector.
1302         embeddedItem.pixelBufferId = index;
1303         break;
1304       }
1305     }
1306
1307     Devel::PixelBuffer pixelBuffer;
1308     if(0u == embeddedItem.pixelBufferId)
1309     {
1310       // The pixel buffer is not in the cache. Create one and cache it.
1311
1312       // Load the image from the url.
1313       pixelBuffer = LoadImageFromFile(description.url);
1314
1315       // Create the cache item.
1316       PixelBufferCacheItem pixelBufferCacheItem;
1317       pixelBufferCacheItem.pixelBuffer = pixelBuffer;
1318       pixelBufferCacheItem.url         = description.url;
1319
1320       // Store the cache item in the cache.
1321       mPixelBufferCache.push_back(std::move(pixelBufferCacheItem));
1322
1323       // Set the pixel buffer id to the embedded item.
1324       embeddedItem.pixelBufferId = mPixelBufferCache.size();
1325     }
1326     else
1327     {
1328       // Retrieve the pixel buffer from the cache to set the pixel format.
1329       pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
1330     }
1331
1332     if(pixelBuffer)
1333     {
1334       // Set the size of the embedded item if it has not been set.
1335       if(0u == embeddedItem.width)
1336       {
1337         embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
1338       }
1339
1340       if(0u == embeddedItem.height)
1341       {
1342         embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
1343       }
1344
1345       // Set the pixel format.
1346       pixelFormat = pixelBuffer.GetPixelFormat();
1347     }
1348   }
1349
1350   // Find if the same embeddedItem has already been created.
1351   unsigned int index = 0u;
1352   for(const auto& item : mEmbeddedItemCache)
1353   {
1354     ++index;
1355     if((item.pixelBufferId == embeddedItem.pixelBufferId) &&
1356        (item.width == embeddedItem.width) &&
1357        (item.height == embeddedItem.height))
1358     {
1359       return index;
1360     }
1361   }
1362
1363   // Cache the embedded item.
1364   mEmbeddedItemCache.PushBack(embeddedItem);
1365
1366   return mEmbeddedItemCache.Count();
1367 }
1368
1369 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
1370 {
1371   mIsAtlasLimitationEnabled = enabled;
1372 }
1373
1374 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
1375 {
1376   return mIsAtlasLimitationEnabled;
1377 }
1378
1379 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
1380 {
1381   return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1382 }
1383
1384 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
1385 {
1386   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
1387 }
1388
1389 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
1390 {
1391   return mCurrentMaximumBlockSizeFitInAtlas;
1392 }
1393
1394 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
1395 {
1396   bool            isChanged        = false;
1397   const Size&     maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
1398   const uint16_t& padding          = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
1399
1400   if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
1401   {
1402     mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
1403     isChanged                          = true;
1404   }
1405
1406   return isChanged;
1407 }
1408
1409 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
1410 {
1411   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
1412   ;
1413 }
1414
1415 void FontClient::Plugin::InitSystemFonts()
1416 {
1417   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1418
1419   FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
1420
1421   if(fontSet)
1422   {
1423     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont);
1424
1425     // Reserve some space to avoid reallocations.
1426     mSystemFonts.reserve(fontSet->nfont);
1427
1428     for(int i = 0u; i < fontSet->nfont; ++i)
1429     {
1430       FcPattern* fontPattern = fontSet->fonts[i];
1431
1432       FontPath path;
1433
1434       // Skip fonts with no path
1435       if(GetFcString(fontPattern, FC_FILE, path))
1436       {
1437         mSystemFonts.push_back(FontDescription());
1438         FontDescription& fontDescription = mSystemFonts.back();
1439
1440         fontDescription.path = std::move(path);
1441
1442         int width  = 0;
1443         int weight = 0;
1444         int slant  = 0;
1445         GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
1446         GetFcInt(fontPattern, FC_WIDTH, width);
1447         GetFcInt(fontPattern, FC_WEIGHT, weight);
1448         GetFcInt(fontPattern, FC_SLANT, slant);
1449         fontDescription.width  = IntToWidthType(width);
1450         fontDescription.weight = IntToWeightType(weight);
1451         fontDescription.slant  = IntToSlantType(slant);
1452
1453         FONT_LOG_DESCRIPTION(fontDescription, "");
1454       }
1455     }
1456
1457     // Destroys the font set created.
1458     FcFontSetDestroy(fontSet);
1459   }
1460 }
1461
1462 bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
1463 {
1464   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1465
1466   FcResult   result = FcResultMatch;
1467   FcPattern* match  = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
1468
1469   const bool matched = nullptr != match;
1470   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  pattern matched : %s\n", (matched ? "true" : "false"));
1471
1472   if(matched)
1473   {
1474     int width  = 0;
1475     int weight = 0;
1476     int slant  = 0;
1477     GetFcString(match, FC_FILE, fontDescription.path);
1478     GetFcString(match, FC_FAMILY, fontDescription.family);
1479     GetFcInt(match, FC_WIDTH, width);
1480     GetFcInt(match, FC_WEIGHT, weight);
1481     GetFcInt(match, FC_SLANT, slant);
1482     fontDescription.width  = IntToWidthType(width);
1483     fontDescription.weight = IntToWeightType(weight);
1484     fontDescription.slant  = IntToSlantType(slant);
1485
1486     // Retrieve the character set and increase the reference counter.
1487     FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
1488     *characterSet = FcCharSetCopy(*characterSet);
1489
1490     // destroyed the matched pattern
1491     FcPatternDestroy(match);
1492     FONT_LOG_DESCRIPTION(fontDescription, "");
1493   }
1494   return matched;
1495 }
1496
1497 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1498 {
1499   FcFontSet* fontset = nullptr;
1500
1501   // create a new pattern.
1502   // a pattern holds a set of names, each name refers to a property of the font
1503   FcPattern* pattern = FcPatternCreate();
1504
1505   if(nullptr != pattern)
1506   {
1507     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1508     FcObjectSet* objectSet = FcObjectSetCreate();
1509
1510     if(nullptr != objectSet)
1511     {
1512       // build an object set from a list of property names
1513       FcObjectSetAdd(objectSet, FC_FILE);
1514       FcObjectSetAdd(objectSet, FC_FAMILY);
1515       FcObjectSetAdd(objectSet, FC_WIDTH);
1516       FcObjectSetAdd(objectSet, FC_WEIGHT);
1517       FcObjectSetAdd(objectSet, FC_SLANT);
1518
1519       // get a list of fonts
1520       // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1521       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.
1522
1523       // clear up the object set
1524       FcObjectSetDestroy(objectSet);
1525     }
1526
1527     // clear up the pattern
1528     FcPatternDestroy(pattern);
1529   }
1530
1531   return fontset;
1532 }
1533
1534 bool FontClient::Plugin::GetFcString(const FcPattern* const pattern,
1535                                      const char* const      n,
1536                                      std::string&           string)
1537 {
1538   FcChar8*       file   = nullptr;
1539   const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
1540
1541   if(FcResultMatch == retVal)
1542   {
1543     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1544     string.assign(reinterpret_cast<const char*>(file));
1545
1546     return true;
1547   }
1548
1549   return false;
1550 }
1551
1552 bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
1553 {
1554   const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
1555
1556   if(FcResultMatch == retVal)
1557   {
1558     return true;
1559   }
1560
1561   return false;
1562 }
1563
1564 FontId FontClient::Plugin::CreateFont(const FontPath& path,
1565                                       PointSize26Dot6 requestedPointSize,
1566                                       FaceIndex       faceIndex,
1567                                       bool            cacheDescription)
1568 {
1569   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1570   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
1571   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1572
1573   FontId id = 0u;
1574
1575   // Create & cache new font face
1576   FT_Face ftFace;
1577   int     error = FT_New_Face(mFreeTypeLibrary,
1578                           path.c_str(),
1579                           0,
1580                           &ftFace);
1581
1582   if(FT_Err_Ok == error)
1583   {
1584     // Check if a font is scalable.
1585     const bool isScalable           = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
1586     const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
1587     const bool hasColorTables       = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
1588     FontId     fontFaceId           = 0u;
1589
1590     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "            isScalable : [%s]\n", (isScalable ? "true" : "false"));
1591     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
1592     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "        hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
1593
1594     // Check to see if the font contains fixed sizes?
1595     if(!isScalable && hasFixedSizedBitmaps)
1596     {
1597       PointSize26Dot6 actualPointSize = 0u;
1598       int             fixedSizeIndex  = 0;
1599       for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
1600       {
1601         const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
1602         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
1603
1604         if(fixedSize >= requestedPointSize)
1605         {
1606           actualPointSize = fixedSize;
1607           break;
1608         }
1609       }
1610
1611       if(0u == actualPointSize)
1612       {
1613         // The requested point size is bigger than the bigest fixed size.
1614         fixedSizeIndex  = ftFace->num_fixed_sizes - 1;
1615         actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
1616       }
1617
1618       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
1619
1620       // Tell Freetype to use this size
1621       error = FT_Select_Size(ftFace, fixedSizeIndex);
1622       if(FT_Err_Ok != error)
1623       {
1624         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
1625       }
1626       else
1627       {
1628         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1629
1630         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1631                             static_cast<float>(ftMetrics.descender) * FROM_266,
1632                             static_cast<float>(ftMetrics.height) * FROM_266,
1633                             static_cast<float>(ftFace->underline_position) * FROM_266,
1634                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1635
1636         const float fixedWidth  = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
1637         const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
1638
1639         // Create the FreeType font face item to cache.
1640         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
1641
1642         // Set the index to the font's id cache.
1643         fontFaceCacheItem.mFontId = mFontIdCache.Count();
1644
1645         // Create the font id item to cache.
1646         FontIdCacheItem fontIdCacheItem;
1647         fontIdCacheItem.type = FontDescription::FACE_FONT;
1648
1649         // Set the index to the FreeType font face cache.
1650         fontIdCacheItem.index = mFontFaceCache.size();
1651         fontFaceId            = fontIdCacheItem.index + 1u;
1652
1653         // Cache the items.
1654         mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
1655         mFontIdCache.PushBack(fontIdCacheItem);
1656
1657         // Set the font id to be returned.
1658         id = mFontIdCache.Count();
1659       }
1660     }
1661     else
1662     {
1663       if(mIsAtlasLimitationEnabled)
1664       {
1665         //There is limitation on block size to fit in predefined atlas size.
1666         //If the block size cannot fit into atlas size, then the system cannot draw block.
1667         //This is workaround to avoid issue in advance
1668         //Decrementing point-size until arriving to maximum allowed block size.
1669         auto        requestedPointSizeBackup = requestedPointSize;
1670         const Size& maxSizeFitInAtlas        = GetCurrentMaximumBlockSizeFitInAtlas();
1671         error                                = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
1672
1673         if(requestedPointSize != requestedPointSizeBackup)
1674         {
1675           DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
1676         }
1677       }
1678       else
1679       {
1680         error = FT_Set_Char_Size(ftFace,
1681                                  0,
1682                                  requestedPointSize,
1683                                  mDpiHorizontal,
1684                                  mDpiVertical);
1685       }
1686
1687       if(FT_Err_Ok == error)
1688       {
1689         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1690
1691         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1692                             static_cast<float>(ftMetrics.descender) * FROM_266,
1693                             static_cast<float>(ftMetrics.height) * FROM_266,
1694                             static_cast<float>(ftFace->underline_position) * FROM_266,
1695                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1696
1697         // Create the FreeType font face item to cache.
1698         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics);
1699
1700         // Set the index to the font's id cache.
1701         fontFaceCacheItem.mFontId = mFontIdCache.Count();
1702
1703         // Create the font id item to cache.
1704         FontIdCacheItem fontIdCacheItem;
1705         fontIdCacheItem.type = FontDescription::FACE_FONT;
1706
1707         // Set the index to the FreeType font face cache.
1708         fontIdCacheItem.index = mFontFaceCache.size();
1709         fontFaceId            = fontIdCacheItem.index + 1u;
1710
1711         // Cache the items.
1712         mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
1713         mFontIdCache.PushBack(fontIdCacheItem);
1714
1715         // Set the font id to be returned.
1716         id = mFontIdCache.Count();
1717       }
1718       else
1719       {
1720         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
1721       }
1722     }
1723
1724     if(0u != fontFaceId)
1725     {
1726       if(cacheDescription)
1727       {
1728         CacheFontPath(ftFace, fontFaceId, requestedPointSize, path);
1729       }
1730     }
1731   }
1732   else
1733   {
1734     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str());
1735   }
1736
1737   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
1738   return id;
1739 }
1740
1741 bool FontClient::Plugin::FindFont(const FontPath& path,
1742                                   PointSize26Dot6 requestedPointSize,
1743                                   FaceIndex       faceIndex,
1744                                   FontId&         fontId) const
1745 {
1746   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1747   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
1748   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1749   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of fonts in the cache : %d\n", mFontFaceCache.size());
1750
1751   fontId = 0u;
1752   for(const auto& cacheItem : mFontFaceCache)
1753   {
1754     if(cacheItem.mRequestedPointSize == requestedPointSize &&
1755        cacheItem.mFaceIndex == faceIndex &&
1756        cacheItem.mPath == path)
1757     {
1758       fontId = cacheItem.mFontId + 1u;
1759
1760       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, id : %d\n", fontId);
1761       return true;
1762     }
1763   }
1764
1765   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found\n");
1766   return false;
1767 }
1768
1769 bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription,
1770                                            FontDescriptionId&     fontDescriptionId)
1771 {
1772   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1773   FONT_LOG_DESCRIPTION(fontDescription, "");
1774   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %d\n", mValidatedFontCache.size());
1775
1776   fontDescriptionId = 0u;
1777
1778   // Fast cut if inputed family is empty.
1779   if(DALI_UNLIKELY(fontDescription.family.empty()))
1780   {
1781     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description not found / fontDescription.family is empty!\n");
1782     return false;
1783   }
1784
1785   // Heuristic optimize code : Compare with latest found item.
1786   if((fontDescription.width == mLatestFoundFontDescription.width) &&
1787      (fontDescription.weight == mLatestFoundFontDescription.weight) &&
1788      (fontDescription.slant == mLatestFoundFontDescription.slant) &&
1789      (fontDescription.family == mLatestFoundFontDescription.family))
1790   {
1791     fontDescriptionId = mLatestFoundFontDescriptionId;
1792
1793     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description same as latest, id : %d\n", fontDescriptionId);
1794     return true;
1795   }
1796
1797   for(const auto& item : mValidatedFontCache)
1798   {
1799     if((fontDescription.width == item.fontDescription.width) &&
1800        (fontDescription.weight == item.fontDescription.weight) &&
1801        (fontDescription.slant == item.fontDescription.slant) &&
1802        (fontDescription.family == item.fontDescription.family))
1803     {
1804       fontDescriptionId = item.index;
1805
1806       mLatestFoundFontDescription   = fontDescription;
1807       mLatestFoundFontDescriptionId = fontDescriptionId;
1808
1809       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description found, id : %d\n", fontDescriptionId);
1810       return true;
1811     }
1812   }
1813
1814   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description not found\n");
1815   return false;
1816 }
1817
1818 bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription,
1819                                               FontList*&             fontList,
1820                                               CharacterSetList*&     characterSetList)
1821 {
1822   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1823   FONT_LOG_DESCRIPTION(fontDescription, "");
1824   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of fallback font lists in the cache : %d\n", mFallbackCache.size());
1825
1826   fontList = nullptr;
1827
1828   for(const auto& item : mFallbackCache)
1829   {
1830     if(!fontDescription.family.empty() &&
1831        (fontDescription.family == item.fontDescription.family) &&
1832        (fontDescription.width == item.fontDescription.width) &&
1833        (fontDescription.weight == item.fontDescription.weight) &&
1834        (fontDescription.slant == item.fontDescription.slant))
1835     {
1836       fontList         = item.fallbackFonts;
1837       characterSetList = item.characterSets;
1838
1839       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list found.\n");
1840       return true;
1841     }
1842   }
1843
1844   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list not found.\n");
1845   return false;
1846 }
1847
1848 bool FontClient::Plugin::FindFont(FontDescriptionId fontDescriptionId,
1849                                   PointSize26Dot6   requestedPointSize,
1850                                   FontCacheIndex&   fontCacheIndex)
1851 {
1852   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1853   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "   fontDescriptionId : %d\n", fontDescriptionId);
1854   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1855
1856   fontCacheIndex = 0u;
1857
1858   const FontDescriptionSizeCacheKey key(fontDescriptionId, requestedPointSize);
1859
1860   // Heuristic optimize code : Compare with latest found item.
1861   if(key == mLatestFoundCacheKey)
1862   {
1863     fontCacheIndex = mLatestFoundCacheIndex;
1864
1865     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font same as latest, index of font cache : %d\n", fontCacheIndex);
1866     return true;
1867   }
1868
1869   const auto& iter = mFontDescriptionSizeCache.find(key);
1870   if(iter != mFontDescriptionSizeCache.cend())
1871   {
1872     fontCacheIndex = iter->second;
1873
1874     mLatestFoundCacheKey   = key;
1875     mLatestFoundCacheIndex = fontCacheIndex;
1876
1877     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, index of font cache : %d\n", fontCacheIndex);
1878     return true;
1879   }
1880
1881   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found.\n");
1882   return false;
1883 }
1884
1885 bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const
1886 {
1887   fontId = 0u;
1888
1889   for(const auto& item : mBitmapFontCache)
1890   {
1891     if(bitmapFont == item.font.name)
1892     {
1893       fontId = item.id + 1u;
1894       return true;
1895     }
1896   }
1897
1898   return false;
1899 }
1900
1901 bool FontClient::Plugin::IsScalable(const FontPath& path)
1902 {
1903   bool isScalable = false;
1904
1905   FT_Face ftFace;
1906   int     error = FT_New_Face(mFreeTypeLibrary,
1907                           path.c_str(),
1908                           0,
1909                           &ftFace);
1910   if(FT_Err_Ok != error)
1911   {
1912     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
1913   }
1914   else
1915   {
1916     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
1917   }
1918
1919   return isScalable;
1920 }
1921
1922 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription)
1923 {
1924   // Create a font pattern.
1925   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1926
1927   FcResult result = FcResultMatch;
1928
1929   // match the pattern
1930   FcPattern* match      = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1931   bool       isScalable = false;
1932
1933   if(match)
1934   {
1935     // Get the path to the font file name.
1936     FontPath path;
1937     GetFcString(match, FC_FILE, path);
1938     isScalable = IsScalable(path);
1939   }
1940   else
1941   {
1942     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1943   }
1944
1945   // Destroys the created patterns.
1946   FcPatternDestroy(match);
1947   FcPatternDestroy(fontFamilyPattern);
1948
1949   return isScalable;
1950 }
1951
1952 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes)
1953 {
1954   // Empty the caller container
1955   sizes.Clear();
1956
1957   FT_Face ftFace;
1958   int     error = FT_New_Face(mFreeTypeLibrary,
1959                           path.c_str(),
1960                           0,
1961                           &ftFace);
1962   if(FT_Err_Ok != error)
1963   {
1964     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
1965   }
1966
1967   // Fetch the number of fixed sizes available
1968   if(ftFace->num_fixed_sizes && ftFace->available_sizes)
1969   {
1970     for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
1971     {
1972       sizes.PushBack(ftFace->available_sizes[i].size);
1973     }
1974   }
1975 }
1976
1977 void FontClient::Plugin::GetFixedSizes(const FontDescription&   fontDescription,
1978                                        Vector<PointSize26Dot6>& sizes)
1979 {
1980   // Create a font pattern.
1981   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1982
1983   FcResult result = FcResultMatch;
1984
1985   // match the pattern
1986   FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1987
1988   if(match)
1989   {
1990     // Get the path to the font file name.
1991     FontPath path;
1992     GetFcString(match, FC_FILE, path);
1993     GetFixedSizes(path, sizes);
1994   }
1995   else
1996   {
1997     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1998   }
1999
2000   // Destroys the created patterns.
2001   FcPatternDestroy(match);
2002   FcPatternDestroy(fontFamilyPattern);
2003 }
2004
2005 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
2006 {
2007   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
2008   if(fontCacheItem != nullptr)
2009   {
2010     return fontCacheItem->HasItalicStyle();
2011   }
2012   return false;
2013 }
2014
2015 void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path)
2016 {
2017   FontDescription description;
2018   description.path   = path;
2019   description.family = std::move(FontFamily(ftFace->family_name));
2020   description.weight = FontWeight::NONE;
2021   description.width  = FontWidth::NONE;
2022   description.slant  = FontSlant::NONE;
2023
2024   // Note FreeType doesn't give too much info to build a proper font style.
2025   if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
2026   {
2027     description.slant = FontSlant::ITALIC;
2028   }
2029   if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
2030   {
2031     description.weight = FontWeight::BOLD;
2032   }
2033
2034   FontDescriptionId fontDescriptionId = 0u;
2035   if(!FindValidatedFont(description,
2036                         fontDescriptionId))
2037   {
2038     FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2039
2040     FcResult   result = FcResultMatch;
2041     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
2042
2043     FcCharSet* characterSet = nullptr;
2044     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
2045
2046     const FontId fontFaceId                  = id - 1u;
2047     mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
2048
2049     // Destroys the created patterns.
2050     FcPatternDestroy(match);
2051     FcPatternDestroy(pattern);
2052
2053     // Add the path to the cache.
2054     description.type = FontDescription::FACE_FONT;
2055     mFontDescriptionCache.push_back(description);
2056
2057     // Set the index to the vector of paths to font file names.
2058     fontDescriptionId = mFontDescriptionCache.size();
2059
2060     // Increase the reference counter and add the character set to the cache.
2061     mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
2062
2063     // Cache the index and the font's description.
2064     mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description),
2065                                                                      fontDescriptionId)));
2066
2067     // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
2068     mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontFaceId);
2069   }
2070 }
2071
2072 void FontClient::Plugin::ClearFallbackCache(std::vector<FallbackCacheItem>& fallbackCache)
2073 {
2074   for(auto& item : fallbackCache)
2075   {
2076     if(nullptr != item.fallbackFonts)
2077     {
2078       delete item.fallbackFonts;
2079     }
2080
2081     if(nullptr != item.characterSets)
2082     {
2083       // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
2084       DestroyCharacterSets(*item.characterSets);
2085       delete item.characterSets;
2086     }
2087   }
2088 }
2089
2090 void FontClient::Plugin::ClearCharacterSetFromFontFaceCache()
2091 {
2092   for(auto& item : mFontFaceCache)
2093   {
2094     FcCharSetDestroy(item.mCharacterSet);
2095     item.mCharacterSet = nullptr;
2096   }
2097 }
2098
2099 } // namespace Dali::TextAbstraction::Internal