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