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