bc62cbaeb39a12d58b5226c4046584d964cae777
[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 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/platform-abstraction.h>
25 #include <dali/internal/adaptor/common/adaptor-impl.h>
26 #include <dali/internal/imaging/common/image-operations.h>
27 #include <dali/internal/text/text-abstraction/plugin/bitmap-font-cache-item.h>
28 #include <dali/internal/text/text-abstraction/plugin/embedded-item.h>
29 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.h>
30 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
31 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
32 #include <dali/public-api/common/dali-vector.h>
33 #include <dali/public-api/common/vector-wrapper.h>
34
35 // EXTERNAL INCLUDES
36 #include <fontconfig/fontconfig.h>
37 #include <algorithm>
38 #include <iterator>
39
40 #if defined(DEBUG_ENABLED)
41
42 // Note, to turn on trace and verbose logging, use "export LOG_FONT_CLIENT=3,true"
43 // Or re-define the following filter using Verbose,true instead of NoLogging,false,
44 // Or, add DALI_LOG_FILTER_ENABLE_TRACE(gFontClientLogFilter) in the code below.
45
46 Dali::Integration::Log::Filter* gFontClientLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
47
48 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)                                                                            \
49   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix "  description; family : [%s]\n", fontDescription.family.c_str()); \
50   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose,                                                                            \
51                 "                 path : [%s]\n"                                                                                 \
52                 "                width : [%s]\n"                                                                                 \
53                 "               weight : [%s]\n"                                                                                 \
54                 "                slant : [%s]\n\n",                                                                              \
55                 fontDescription.path.c_str(),                                                                                    \
56                 FontWidth::Name[fontDescription.width],                                                                          \
57                 FontWeight::Name[fontDescription.weight],                                                                        \
58                 FontSlant::Name[fontDescription.slant])
59
60 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
61   DALI_LOG_INFO(gFontClientLogFilter, Debug::General,               \
62                 "           character : %p\n"                       \
63                 "  requestedPointSize : %d\n"                       \
64                 "         preferColor : %s\n",                      \
65                 charcode,                                           \
66                 requestedPointSize,                                 \
67                 (preferColor ? "true" : "false"))
68
69 #else
70
71 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
72 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
73
74 #endif
75
76 namespace
77 {
78 /**
79  * Conversion from Fractional26.6 to float
80  */
81 const float FROM_266        = 1.0f / 64.0f;
82 const float POINTS_PER_INCH = 72.f;
83
84 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
85
86 } // namespace
87
88 using Dali::Vector;
89
90 namespace Dali::TextAbstraction::Internal
91 {
92 namespace
93 {
94 /**
95  * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block
96  *
97  * @param[in/out] ftFace Face type object.
98  * @param[in] horizontalDpi The horizontal dpi.
99  * @param[in] verticalDpi The vertical dpi.
100  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
101  * @param[in] requestedPointSize The requested point-size.
102  * @return whether the  ftFace's block can fit into atlas
103  */
104 bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize)
105 {
106   bool isFit = false;
107
108   error = FT_Set_Char_Size(ftFace,
109                            0,
110                            requestedPointSize,
111                            horizontalDpi,
112                            verticalDpi);
113
114   if(error == FT_Err_Ok)
115   {
116     //Check width and height of block for requestedPointSize
117     //If the width or height is greater than the maximum-size then decrement by one unit of point-size.
118     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)
119     {
120       isFit = true;
121     }
122   }
123
124   return isFit;
125 }
126
127 /**
128  * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi
129  *
130  * @param[in/out] ftFace Face type object.
131  * @param[in] horizontalDpi The horizontal dpi.
132  * @param[in] verticalDpi The vertical dpi.
133  * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas
134  * @param[in/out] requestedPointSize The requested point-size.
135  * @return FreeType error code. 0 means success when requesting the nominal size (in points).
136  */
137 int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize)
138 {
139   //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search.
140   const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
141   bool            canFitInAtlas;
142   int             error; // FreeType error code.
143
144   canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
145   if(FT_Err_Ok != error)
146   {
147     return error;
148   }
149
150   if(!canFitInAtlas)
151   {
152     //Exponential search
153     uint32_t exponentialDecrement = 1;
154
155     while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit * exponentialDecrement)
156     {
157       requestedPointSize -= (pointSizePerOneUnit * exponentialDecrement);
158       canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
159       if(FT_Err_Ok != error)
160       {
161         return error;
162       }
163
164       exponentialDecrement *= 2;
165     }
166
167     //Binary search
168     uint32_t minPointSize;
169     uint32_t maxPointSize;
170
171     if(canFitInAtlas)
172     {
173       exponentialDecrement /= 2;
174       minPointSize = requestedPointSize;
175       maxPointSize = requestedPointSize + (pointSizePerOneUnit * exponentialDecrement);
176     }
177     else
178     {
179       minPointSize = 0;
180       maxPointSize = requestedPointSize;
181     }
182
183     while(minPointSize < maxPointSize)
184     {
185       requestedPointSize = ((maxPointSize / pointSizePerOneUnit - minPointSize / pointSizePerOneUnit) / 2) * pointSizePerOneUnit + minPointSize;
186       canFitInAtlas      = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize);
187       if(FT_Err_Ok != error)
188       {
189         return error;
190       }
191
192       if(canFitInAtlas)
193       {
194         if(minPointSize == requestedPointSize)
195         {
196           //Found targeted point-size
197           return error;
198         }
199
200         minPointSize = requestedPointSize;
201       }
202       else
203       {
204         maxPointSize = requestedPointSize;
205       }
206     }
207   }
208
209   return error;
210 }
211
212 } // namespace
213
214 FontClient::Plugin::Plugin(unsigned int horizontalDpi,
215                            unsigned int verticalDpi)
216 : mFreeTypeLibrary(nullptr),
217   mDpiHorizontal(horizontalDpi),
218   mDpiVertical(verticalDpi),
219   mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED),
220   mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS),
221   mVectorFontCache(nullptr),
222   mCacheHandler(new CacheHandler())
223 {
224   int error = FT_Init_FreeType(&mFreeTypeLibrary);
225   if(FT_Err_Ok != error)
226   {
227     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Init error: %d\n", error);
228   }
229
230 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
231   mVectorFontCache = new VectorFontCache(mFreeTypeLibrary);
232 #endif
233 }
234
235 FontClient::Plugin::~Plugin()
236 {
237   // Delete cache hanlder before remove mFreeTypeLibrary
238   delete mCacheHandler;
239
240 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
241   delete mVectorFontCache;
242 #endif
243
244   FT_Done_FreeType(mFreeTypeLibrary);
245 }
246
247 void FontClient::Plugin::ClearCache() const
248 {
249   mCacheHandler->ClearCache();
250 }
251
252 void FontClient::Plugin::SetDpi(unsigned int horizontalDpi,
253                                 unsigned int verticalDpi)
254 {
255   mDpiHorizontal = horizontalDpi;
256   mDpiVertical   = verticalDpi;
257 }
258
259 void FontClient::Plugin::ResetSystemDefaults() const
260 {
261   mCacheHandler->ResetSystemDefaults();
262 }
263
264 void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription) const
265 {
266   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
267
268   mCacheHandler->InitDefaultFontDescription();
269   fontDescription = mCacheHandler->mDefaultFontDescription;
270
271   FONT_LOG_DESCRIPTION(fontDescription, "");
272 }
273
274 void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts) const
275 {
276   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
277
278   mCacheHandler->InitDefaultFonts();
279   defaultFonts = mCacheHandler->mDefaultFonts;
280
281   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of default fonts : [%d]\n", mCacheHandler->mDefaultFonts.size());
282 }
283
284 void FontClient::Plugin::GetSystemFonts(FontList& systemFonts) const
285 {
286   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
287
288   mCacheHandler->InitSystemFonts();
289   systemFonts = mCacheHandler->mSystemFonts;
290
291   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : [%d]\n", mCacheHandler->mSystemFonts.size());
292 }
293
294 void FontClient::Plugin::GetDescription(FontId           fontId,
295                                         FontDescription& fontDescription) const
296 {
297   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
298   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
299
300   if((fontId > 0u) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
301   {
302     const auto& fontIdCacheItem = mCacheHandler->mFontIdCache[fontId - 1u];
303     switch(fontIdCacheItem.type)
304     {
305       case FontDescription::FACE_FONT:
306       {
307         for(const auto& item : mCacheHandler->mFontDescriptionSizeCache)
308         {
309           if(item.second == fontIdCacheItem.index)
310           {
311             fontDescription = *(mCacheHandler->mFontDescriptionCache.begin() + item.first.fontDescriptionId - 1u);
312
313             FONT_LOG_DESCRIPTION(fontDescription, "");
314             return;
315           }
316         }
317         break;
318       }
319       case FontDescription::BITMAP_FONT:
320       {
321         fontDescription.type   = FontDescription::BITMAP_FONT;
322         fontDescription.family = mCacheHandler->mBitmapFontCache[fontIdCacheItem.index].font.name;
323         break;
324       }
325       default:
326       {
327         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Invalid type of font\n");
328         fontDescription.type = FontDescription::INVALID;
329         fontDescription.family.clear();
330       }
331     }
332   }
333
334   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  No description found for the font id %d\n", fontId);
335 }
336
337 PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId fontId) const
338 {
339   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
340   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
341
342   PointSize26Dot6               pointSize     = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
343   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
344   if(fontCacheItem != nullptr)
345   {
346     pointSize = fontCacheItem->GetPointSize();
347   }
348   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  point size : %d\n", pointSize);
349
350   return pointSize;
351 }
352
353 bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character) const
354 {
355   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
356   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "    font id : %d\n", fontId);
357   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  character : %p\n", character);
358
359   bool isSupported   = false;
360   auto fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
361   if(fontCacheItem != nullptr)
362   {
363     isSupported = fontCacheItem->IsCharacterSupported(character); // May cache
364   }
365
366   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  is supported : %s\n", (isSupported ? "true" : "false"));
367   return isSupported;
368 }
369
370 const FontCacheItemInterface* FontClient::Plugin::GetCachedFontItem(FontId fontId) const
371 {
372   if((fontId > 0u) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
373   {
374     const auto& fontIdCacheItem = mCacheHandler->mFontIdCache[fontId - 1u];
375     switch(fontIdCacheItem.type)
376     {
377       case FontDescription::FACE_FONT:
378       {
379         return &mCacheHandler->mFontFaceCache[fontIdCacheItem.index];
380       }
381       case FontDescription::BITMAP_FONT:
382       {
383         return &mCacheHandler->mBitmapFontCache[fontIdCacheItem.index];
384       }
385       default:
386       {
387         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Invalid type of font\n");
388       }
389     }
390   }
391   return nullptr;
392 }
393
394 FontId FontClient::Plugin::FindFontForCharacter(const FontList&         fontList,
395                                                 const CharacterSetList& characterSetList,
396                                                 Character               character,
397                                                 PointSize26Dot6         requestedPointSize,
398                                                 bool                    preferColor) const
399 {
400   DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets.");
401   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
402   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "           character : %p\n", character);
403   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
404   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "         preferColor : %s\n", (preferColor ? "true" : "false"));
405
406   FontId fontId     = 0u;
407   bool   foundColor = false;
408
409   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of fonts : %d\n", fontList.size());
410
411   // Traverse the list of fonts.
412   // Check for each font if supports the character.
413   for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index)
414   {
415     const FontDescription& description  = fontList[index];
416     const FcCharSet* const characterSet = characterSetList[index];
417
418     FONT_LOG_DESCRIPTION(description, "");
419
420     bool foundInRanges = false;
421     if(nullptr != characterSet)
422     {
423       foundInRanges = FcCharSetHasChar(characterSet, character);
424     }
425
426     if(foundInRanges)
427     {
428       fontId = GetFontId(description, requestedPointSize, 0u);
429
430       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "     font id : %d\n", fontId);
431
432       if(preferColor)
433       {
434         if((fontId > 0) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
435         {
436           const FontFaceCacheItem& item = mCacheHandler->mFontFaceCache[mCacheHandler->mFontIdCache[fontId - 1u].index];
437
438           foundColor = item.mHasColorTables;
439         }
440
441         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  foundColor : %s\n", (foundColor ? "true" : "false"));
442       }
443
444       // Keep going unless we prefer a different (color) font.
445       if(!preferColor || foundColor)
446       {
447         break;
448       }
449     }
450   }
451
452   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
453   return fontId;
454 }
455
456 FontId FontClient::Plugin::FindDefaultFont(Character       charcode,
457                                            PointSize26Dot6 requestedPointSize,
458                                            bool            preferColor) const
459 {
460   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
461   FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
462
463   FontId fontId(0);
464
465   // Create the list of default fonts if it has not been created.
466   mCacheHandler->InitDefaultFonts();
467   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of default fonts : %d\n", mCacheHandler->mDefaultFonts.size());
468
469   // Traverse the list of default fonts.
470   // Check for each default font if supports the character.
471   fontId = FindFontForCharacter(mCacheHandler->mDefaultFonts, mCacheHandler->mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor);
472
473   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
474   return fontId;
475 }
476
477 FontId FontClient::Plugin::FindFallbackFont(Character              charcode,
478                                             const FontDescription& preferredFontDescription,
479                                             PointSize26Dot6        requestedPointSize,
480                                             bool                   preferColor) const
481 {
482   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
483   FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor);
484
485   // The font id to be returned.
486   FontId fontId = 0u;
487
488   FontDescription fontDescription;
489
490   // Fill the font description with the preferred font description and complete with the defaults.
491   fontDescription.family = preferredFontDescription.family.empty() ? DefaultFontFamily() : preferredFontDescription.family;
492   fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? DefaultFontWeight() : preferredFontDescription.weight);
493   fontDescription.width  = ((FontWidth::NONE == preferredFontDescription.width) ? DefaultFontWidth() : preferredFontDescription.width);
494   fontDescription.slant  = ((FontSlant::NONE == preferredFontDescription.slant) ? DefaultFontSlant() : preferredFontDescription.slant);
495
496   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  preferredFontDescription --> fontDescription\n");
497   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str());
498   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]);
499   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]);
500   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]);
501
502   // Check first if the font's description has been queried before.
503   FontList*         fontList         = nullptr;
504   CharacterSetList* characterSetList = nullptr;
505
506   if(!mCacheHandler->FindFallbackFontList(fontDescription, fontList, characterSetList))
507   {
508     mCacheHandler->CacheFallbackFontList(std::move(fontDescription), fontList, characterSetList);
509   }
510
511   if(fontList && characterSetList)
512   {
513     fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor);
514   }
515
516   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
517   return fontId;
518 }
519
520 FontId FontClient::Plugin::GetFontIdByPath(const FontPath& path,
521                                            PointSize26Dot6 requestedPointSize,
522                                            FaceIndex       faceIndex,
523                                            bool            cacheDescription) const
524 {
525   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
526   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
527   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
528
529   FontId id = 0u;
530
531   if(nullptr != mFreeTypeLibrary)
532   {
533     FontId foundId = 0u;
534     if(mCacheHandler->FindFontByPath(path, requestedPointSize, faceIndex, foundId))
535     {
536       id = foundId;
537     }
538     else
539     {
540       id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription);
541     }
542   }
543
544   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", id);
545   return id;
546 }
547
548 FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription,
549                                      PointSize26Dot6        requestedPointSize,
550                                      FaceIndex              faceIndex) const
551 {
552   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
553   FONT_LOG_DESCRIPTION(fontDescription, "");
554
555   // Special case when font Description don't have family information.
556   // In this case, we just use default description family and path.
557   const FontDescription& realFontDescription = fontDescription.family.empty() ? FontDescription(mCacheHandler->mDefaultFontDescription.path,
558                                                                                                 mCacheHandler->mDefaultFontDescription.family,
559                                                                                                 fontDescription.width,
560                                                                                                 fontDescription.weight,
561                                                                                                 fontDescription.slant,
562                                                                                                 fontDescription.type)
563                                                                               : fontDescription;
564
565   FONT_LOG_DESCRIPTION(realFontDescription, "");
566   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "   requestedPointSize : %d\n", requestedPointSize);
567
568   // This method uses three vectors which caches:
569   // * The bitmap font cache
570   // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
571   // * The path to font file names.
572   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
573
574   // 1) Checks if the font description matches with a previously loaded bitmap font.
575   //    Returns if a font is found.
576   // 2) Checks in the cache if the font's description has been validated before.
577   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
578   //    retrieves using font config a path to a font file name which matches with the
579   //    font's description. The path is stored in the cache.
580   //
581   // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to
582   //    font file names' exists. If exists, it gets the font id. If it doesn't it calls
583   //    the GetFontId() method with the path to the font file name and the point size to
584   //    get the font id.
585
586   // The font id to be returned.
587   FontId fontId = 0u;
588
589   // Check first if the font description matches with a previously loaded bitmap font.
590   if(mCacheHandler->FindBitmapFont(realFontDescription.family, fontId))
591   {
592     return fontId;
593   }
594
595   // Check if the font's description have been validated before.
596   FontDescriptionId fontDescriptionId = 0u;
597
598   if(!mCacheHandler->FindValidatedFont(realFontDescription, fontDescriptionId))
599   {
600     // Use font config to validate the font's description.
601     mCacheHandler->ValidateFont(realFontDescription, fontDescriptionId);
602   }
603
604   using FontCacheIndex          = CacheHandler::FontCacheIndex;
605   FontCacheIndex fontCacheIndex = 0u;
606   // Check if exists a pair 'fontDescriptionId, requestedPointSize' in the cache.
607   if(!mCacheHandler->FindFont(fontDescriptionId, requestedPointSize, fontCacheIndex))
608   {
609     // Retrieve the font file name path.
610     const FontDescription& description = *(mCacheHandler->mFontDescriptionCache.begin() + fontDescriptionId - 1u);
611
612     // Retrieve the font id. Do not cache the description as it has been already cached.
613     // Note : CacheFontPath() API call ValidateFont() + setup CharacterSet + cache the font description.
614     // So set cacheDescription=false, that we don't call CacheFontPath().
615     fontId = GetFontIdByPath(description.path, requestedPointSize, faceIndex, false);
616
617     if((fontId > 0u) && (fontId - 1u < mCacheHandler->mFontIdCache.size()))
618     {
619       fontCacheIndex                                              = mCacheHandler->mFontIdCache[fontId - 1u].index;
620       mCacheHandler->mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCacheHandler->mCharacterSetCache[fontDescriptionId - 1u]);
621
622       // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
623       mCacheHandler->CacheFontDescriptionSize(fontDescriptionId, requestedPointSize, fontCacheIndex);
624     }
625   }
626   else
627   {
628     fontId = mCacheHandler->mFontFaceCache[fontCacheIndex].mFontId + 1u;
629   }
630
631   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
632   return fontId;
633 }
634
635 FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont) const
636 {
637   // The font id to be returned.
638   FontId fontId = 0u;
639   if(!mCacheHandler->FindBitmapFont(bitmapFont.name, fontId))
640   {
641     BitmapFontCacheItem bitmapFontCacheItem(bitmapFont);
642
643     fontId = mCacheHandler->CacheBitmapFontCacheItem(std::move(bitmapFontCacheItem));
644   }
645   return fontId;
646 }
647
648 void FontClient::Plugin::GetFontMetrics(FontId       fontId,
649                                         FontMetrics& metrics) const
650 {
651   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
652   if(fontCacheItem != nullptr)
653   {
654     fontCacheItem->GetFontMetrics(metrics, mDpiVertical);
655   }
656 }
657
658 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
659                                              Character charcode) const
660 {
661   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
662   if(fontCacheItem != nullptr)
663   {
664     return fontCacheItem->GetGlyphIndex(charcode);
665   }
666
667   return 0u;
668 }
669
670 GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId    fontId,
671                                              Character charcode,
672                                              Character variantSelector) const
673 {
674   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
675   if(fontCacheItem != nullptr)
676   {
677     return fontCacheItem->GetGlyphIndex(charcode, variantSelector);
678   }
679
680   return 0u;
681 }
682
683 bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array,
684                                          uint32_t   size,
685                                          GlyphType  type,
686                                          bool       horizontal) const
687 {
688   if(VECTOR_GLYPH == type)
689   {
690     return GetVectorMetrics(array, size, horizontal);
691   }
692
693   return GetBitmapMetrics(array, size, horizontal);
694 }
695
696 bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array,
697                                           uint32_t   size,
698                                           bool       horizontal) const
699 {
700   bool success(size > 0u);
701
702   for(unsigned int i = 0; i < size; ++i)
703   {
704     GlyphInfo& glyph = array[i];
705
706     const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(glyph.fontId);
707     if(fontCacheItem != nullptr)
708     {
709       success &= fontCacheItem->GetGlyphMetrics(glyph, mDpiVertical, horizontal);
710     }
711     // Check if it's an embedded image.
712     else if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mCacheHandler->mEmbeddedItemCache.size()))
713     {
714       mCacheHandler->mEmbeddedItemCache[glyph.index - 1u].GetGlyphMetrics(glyph);
715     }
716     else
717     {
718       success = false;
719     }
720   }
721
722   return success;
723 }
724
725 bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array,
726                                           uint32_t   size,
727                                           bool       horizontal) const
728 {
729 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
730   bool success(true);
731
732   for(unsigned int i = 0u; i < size; ++i)
733   {
734     FontId fontId = array[i].fontId;
735
736     if((fontId > 0u) &&
737        (fontId - 1u) < mCacheHandler->mFontIdCache.size())
738     {
739       FontFaceCacheItem& font = mCacheHandler->mFontFaceCache[mCacheHandler->mFontIdCache[fontId - 1u].index];
740
741       if(!font.mVectorFontId)
742       {
743         font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
744       }
745
746       mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]);
747
748       // Vector metrics are in EMs, convert to pixels
749       const float scale = (static_cast<float>(font.mRequestedPointSize) * FROM_266) * static_cast<float>(mDpiVertical) / POINTS_PER_INCH;
750       array[i].width *= scale;
751       array[i].height *= scale;
752       array[i].xBearing *= scale;
753       array[i].yBearing *= scale;
754       array[i].advance *= scale;
755     }
756     else
757     {
758       success = false;
759     }
760   }
761
762   return success;
763 #else
764   return false;
765 #endif
766 }
767
768 void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth) const
769 {
770   data.isColorBitmap                          = false;
771   data.isColorEmoji                           = false;
772   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
773   if(fontCacheItem != nullptr)
774   {
775     fontCacheItem->CreateBitmap(glyphIndex, data, outlineWidth, isItalicRequired, isBoldRequired);
776   }
777   else if((0u != glyphIndex) && (glyphIndex <= mCacheHandler->mEmbeddedItemCache.size()))
778   {
779     // It's an embedded item.
780     mCacheHandler->mEmbeddedItemCache[glyphIndex - 1u].CreateBitmap(mCacheHandler->mPixelBufferCache, data);
781   }
782 }
783
784 PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth) const
785 {
786   TextAbstraction::FontClient::GlyphBufferData data;
787
788   CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth);
789
790   // If data is compressed or not owned buffer, copy this.
791   if(!data.isBufferOwned || data.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION)
792   {
793     uint8_t* newBuffer = (uint8_t*)malloc(data.width * data.height * Pixel::GetBytesPerPixel(data.format));
794     TextAbstraction::FontClient::GlyphBufferData::Decompress(data, newBuffer);
795     if(data.isBufferOwned)
796     {
797       free(data.buffer);
798     }
799
800     data.buffer          = newBuffer;
801     data.isBufferOwned   = true;
802     data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
803   }
804
805   return PixelData::New(data.buffer,
806                         data.width * data.height * Pixel::GetBytesPerPixel(data.format),
807                         data.width,
808                         data.height,
809                         data.format,
810                         PixelData::FREE);
811 }
812
813 void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight) const
814 {
815   blob       = nullptr;
816   blobLength = 0;
817
818 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
819   if((fontId > 0u) &&
820      (fontId - 1u < mCacheHandler->mFontIdCache.size()))
821   {
822     using FontCacheIndex                = CacheHandler::FontCacheIndex;
823     const FontCacheIndex fontCacheIndex = mCacheHandler->mFontIdCache[fontId - 1u].index;
824     FontFaceCacheItem&   font           = mCacheHandler->mFontFaceCache[fontCacheIndex];
825
826     if(!font.mVectorFontId)
827     {
828       font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath);
829     }
830
831     mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontCacheIndex, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
832   }
833 #endif
834 }
835
836 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize) const
837 {
838   using EllipsisCacheIndex              = CacheHandler::EllipsisCacheIndex;
839   using EllipsisItem                    = CacheHandler::EllipsisItem;
840   EllipsisCacheIndex ellipsisCacheIndex = 0u;
841
842   if(!mCacheHandler->FindEllipsis(requestedPointSize, ellipsisCacheIndex))
843   {
844     // No glyph has been found. Create one.
845     EllipsisItem item;
846
847     item.requestedPointSize = requestedPointSize;
848     item.index              = ellipsisCacheIndex;
849
850     // Find a font for the ellipsis glyph.
851     item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER,
852                                         requestedPointSize,
853                                         false);
854
855     // Set the character index to access the glyph inside the font.
856     item.glyph.index = GetGlyphIndex(item.glyph.fontId, ELLIPSIS_CHARACTER);
857
858     // Get glyph informations.
859     GetBitmapMetrics(&item.glyph, 1u, true);
860
861     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
862     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
863
864     ellipsisCacheIndex = mCacheHandler->CacheEllipsis(std::move(item));
865   }
866   return mCacheHandler->mEllipsisCache[ellipsisCacheIndex].glyph;
867 }
868
869 bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex) const
870 {
871   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
872   return fontCacheItem && fontCacheItem->IsColorGlyph(glyphIndex);
873 }
874
875 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId) const
876 {
877   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
878   if(fontCacheItem != nullptr)
879   {
880     return fontCacheItem->GetTypeface();
881   }
882   return nullptr;
883 }
884
885 FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId) const
886 {
887   const FontId index = fontId - 1u;
888   if((fontId > 0u) && (index < mCacheHandler->mFontIdCache.size()))
889   {
890     return mCacheHandler->mFontIdCache[index].type;
891   }
892   return FontDescription::INVALID;
893 }
894
895 bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path)
896 {
897   // nullptr as first parameter means the current configuration is used.
898   return FcConfigAppFontAddDir(nullptr, reinterpret_cast<const FcChar8*>(path.c_str()));
899 }
900
901 HarfBuzzFontHandle FontClient::Plugin::GetHarfBuzzFont(FontId fontId) const
902 {
903   FontCacheItemInterface* fontCacheItem = const_cast<FontCacheItemInterface*>(GetCachedFontItem(fontId));
904   if(fontCacheItem != nullptr)
905   {
906     return fontCacheItem->GetHarfBuzzFont(mDpiHorizontal, mDpiVertical); // May cache
907   }
908   return nullptr;
909 }
910
911 GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat) const
912 {
913   EmbeddedItem embeddedItem;
914
915   embeddedItem.pixelBufferId = 0u;
916   embeddedItem.width         = description.width;
917   embeddedItem.height        = description.height;
918
919   pixelFormat = Pixel::A8;
920
921   if(!description.url.empty())
922   {
923     // Check if the url is in the cache.
924     Devel::PixelBuffer pixelBuffer;
925     if(!mCacheHandler->FindEmbeddedPixelBufferId(description.url, embeddedItem.pixelBufferId))
926     {
927       // The pixel buffer is not in the cache. Create one and cache it.
928       embeddedItem.pixelBufferId = mCacheHandler->CacheEmbeddedPixelBuffer(description.url);
929     }
930
931     if((embeddedItem.pixelBufferId > 0u) && (embeddedItem.pixelBufferId - 1u) < mCacheHandler->mPixelBufferCache.size())
932     {
933       // Retrieve the pixel buffer from the cache to set the pixel format.
934       pixelBuffer = mCacheHandler->mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer;
935     }
936
937     if(pixelBuffer)
938     {
939       // Set the size of the embedded item if it has not been set.
940       if(0u == embeddedItem.width)
941       {
942         embeddedItem.width = static_cast<unsigned int>(pixelBuffer.GetWidth());
943       }
944
945       if(0u == embeddedItem.height)
946       {
947         embeddedItem.height = static_cast<unsigned int>(pixelBuffer.GetHeight());
948       }
949
950       // Set the pixel format.
951       pixelFormat = pixelBuffer.GetPixelFormat();
952     }
953   }
954
955   // Find if the same embeddedItem has already been created.
956   GlyphIndex index = 0u;
957   if(!mCacheHandler->FindEmbeddedItem(embeddedItem.pixelBufferId, embeddedItem.width, embeddedItem.height, index))
958   {
959     index = mCacheHandler->CacheEmbeddedItem(std::move(embeddedItem));
960   }
961   return index;
962 }
963
964 void FontClient::Plugin::EnableAtlasLimitation(bool enabled)
965 {
966   mIsAtlasLimitationEnabled = enabled;
967 }
968
969 bool FontClient::Plugin::IsAtlasLimitationEnabled() const
970 {
971   return mIsAtlasLimitationEnabled;
972 }
973
974 Size FontClient::Plugin::GetMaximumTextAtlasSize() const
975 {
976   return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
977 }
978
979 Size FontClient::Plugin::GetDefaultTextAtlasSize() const
980 {
981   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
982 }
983
984 Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const
985 {
986   return mCurrentMaximumBlockSizeFitInAtlas;
987 }
988
989 bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
990 {
991   bool            isChanged        = false;
992   const Size&     maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
993   const uint16_t& padding          = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK;
994
995   if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding)
996   {
997     mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas;
998     isChanged                          = true;
999   }
1000
1001   return isChanged;
1002 }
1003
1004 uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const
1005 {
1006   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
1007   ;
1008 }
1009
1010 FontId FontClient::Plugin::CreateFont(const FontPath& path,
1011                                       PointSize26Dot6 requestedPointSize,
1012                                       FaceIndex       faceIndex,
1013                                       bool            cacheDescription) const
1014 {
1015   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
1016   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
1017   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
1018
1019   FontId fontId = 0u;
1020
1021   // Create & cache new font face
1022   FT_Face ftFace;
1023   int     error = FT_New_Face(mFreeTypeLibrary,
1024                           path.c_str(),
1025                           0,
1026                           &ftFace);
1027
1028   if(FT_Err_Ok == error)
1029   {
1030     // Check if a font is scalable.
1031     const bool isScalable           = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE));
1032     const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes);
1033     const bool hasColorTables       = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR));
1034
1035     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "            isScalable : [%s]\n", (isScalable ? "true" : "false"));
1036     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false"));
1037     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "        hasColorTables : [%s]\n", (hasColorTables ? "true" : "false"));
1038
1039     // Check to see if the font contains fixed sizes?
1040     if(!isScalable && hasFixedSizedBitmaps)
1041     {
1042       PointSize26Dot6 actualPointSize = 0u;
1043       int             fixedSizeIndex  = 0;
1044       for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex)
1045       {
1046         const PointSize26Dot6 fixedSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
1047         DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize);
1048
1049         if(fixedSize >= requestedPointSize)
1050         {
1051           actualPointSize = fixedSize;
1052           break;
1053         }
1054       }
1055
1056       if(0u == actualPointSize)
1057       {
1058         // The requested point size is bigger than the bigest fixed size.
1059         fixedSizeIndex  = ftFace->num_fixed_sizes - 1;
1060         actualPointSize = static_cast<PointSize26Dot6>(ftFace->available_sizes[fixedSizeIndex].size);
1061       }
1062
1063       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize);
1064
1065       // Tell Freetype to use this size
1066       error = FT_Select_Size(ftFace, fixedSizeIndex);
1067       if(FT_Err_Ok != error)
1068       {
1069         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error);
1070       }
1071       else
1072       {
1073         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1074
1075         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1076                             static_cast<float>(ftMetrics.descender) * FROM_266,
1077                             static_cast<float>(ftMetrics.height) * FROM_266,
1078                             static_cast<float>(ftFace->underline_position) * FROM_266,
1079                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1080
1081         const float fixedWidth  = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].width);
1082         const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
1083
1084         // Create the FreeType font face item to cache.
1085         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
1086
1087         fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
1088       }
1089     }
1090     else
1091     {
1092       if(mIsAtlasLimitationEnabled)
1093       {
1094         //There is limitation on block size to fit in predefined atlas size.
1095         //If the block size cannot fit into atlas size, then the system cannot draw block.
1096         //This is workaround to avoid issue in advance
1097         //Decrementing point-size until arriving to maximum allowed block size.
1098         auto        requestedPointSizeBackup = requestedPointSize;
1099         const Size& maxSizeFitInAtlas        = GetCurrentMaximumBlockSizeFitInAtlas();
1100         error                                = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize);
1101
1102         if(requestedPointSize != requestedPointSizeBackup)
1103         {
1104           DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize);
1105         }
1106       }
1107       else
1108       {
1109         error = FT_Set_Char_Size(ftFace,
1110                                  0,
1111                                  requestedPointSize,
1112                                  mDpiHorizontal,
1113                                  mDpiVertical);
1114       }
1115
1116       if(FT_Err_Ok == error)
1117       {
1118         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1119
1120         FontMetrics metrics(static_cast<float>(ftMetrics.ascender) * FROM_266,
1121                             static_cast<float>(ftMetrics.descender) * FROM_266,
1122                             static_cast<float>(ftMetrics.height) * FROM_266,
1123                             static_cast<float>(ftFace->underline_position) * FROM_266,
1124                             static_cast<float>(ftFace->underline_thickness) * FROM_266);
1125
1126         // Create the FreeType font face item to cache.
1127         FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics);
1128
1129         fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
1130       }
1131       else
1132       {
1133         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize);
1134       }
1135     }
1136
1137     if(0u != fontId)
1138     {
1139       if(cacheDescription)
1140       {
1141         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  Cache Font Path at font id : %d [%s]\n", fontId, path.c_str());
1142         mCacheHandler->CacheFontPath(ftFace, fontId, requestedPointSize, path);
1143       }
1144     }
1145   }
1146   else
1147   {
1148     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str());
1149   }
1150
1151   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font id : %d\n", fontId);
1152   return fontId;
1153 }
1154
1155 bool FontClient::Plugin::IsScalable(const FontPath& path) const
1156 {
1157   bool isScalable = false;
1158
1159   FT_Face ftFace = nullptr;
1160   int     error  = FT_New_Face(mFreeTypeLibrary,
1161                           path.c_str(),
1162                           0,
1163                           &ftFace);
1164   if(FT_Err_Ok != error)
1165   {
1166     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str());
1167   }
1168   else
1169   {
1170     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
1171   }
1172
1173   if(ftFace)
1174   {
1175     FT_Done_Face(ftFace);
1176   }
1177
1178   return isScalable;
1179 }
1180
1181 bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription) const
1182 {
1183   // Create a font pattern.
1184   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1185
1186   FcResult result = FcResultMatch;
1187
1188   // match the pattern
1189   FcPattern* match      = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1190   bool       isScalable = false;
1191
1192   if(match)
1193   {
1194     // Get the path to the font file name.
1195     FontPath path;
1196     GetFcString(match, FC_FILE, path);
1197     isScalable = IsScalable(path);
1198   }
1199   else
1200   {
1201     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1202   }
1203
1204   // Destroys the created patterns.
1205   FcPatternDestroy(match);
1206   FcPatternDestroy(fontFamilyPattern);
1207
1208   return isScalable;
1209 }
1210
1211 void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector<PointSize26Dot6>& sizes) const
1212 {
1213   // Empty the caller container
1214   sizes.Clear();
1215
1216   FT_Face ftFace = nullptr;
1217   int     error  = FT_New_Face(mFreeTypeLibrary,
1218                           path.c_str(),
1219                           0,
1220                           &ftFace);
1221   if(FT_Err_Ok != error)
1222   {
1223     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str());
1224   }
1225
1226   if(ftFace)
1227   {
1228     // Fetch the number of fixed sizes available
1229     if(ftFace->num_fixed_sizes && ftFace->available_sizes)
1230     {
1231       for(int i = 0; i < ftFace->num_fixed_sizes; ++i)
1232       {
1233         sizes.PushBack(ftFace->available_sizes[i].size);
1234       }
1235     }
1236
1237     FT_Done_Face(ftFace);
1238   }
1239 }
1240
1241 void FontClient::Plugin::GetFixedSizes(const FontDescription&   fontDescription,
1242                                        Vector<PointSize26Dot6>& sizes) const
1243 {
1244   // Create a font pattern.
1245   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1246
1247   FcResult result = FcResultMatch;
1248
1249   // match the pattern
1250   FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy.
1251
1252   if(match)
1253   {
1254     // Get the path to the font file name.
1255     FontPath path;
1256     GetFcString(match, FC_FILE, path);
1257     GetFixedSizes(path, sizes);
1258   }
1259   else
1260   {
1261     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str());
1262   }
1263
1264   // Destroys the created patterns.
1265   FcPatternDestroy(match);
1266   FcPatternDestroy(fontFamilyPattern);
1267 }
1268
1269 bool FontClient::Plugin::HasItalicStyle(FontId fontId) const
1270 {
1271   const FontCacheItemInterface* fontCacheItem = GetCachedFontItem(fontId);
1272   if(fontCacheItem != nullptr)
1273   {
1274     return fontCacheItem->HasItalicStyle();
1275   }
1276   return false;
1277 }
1278
1279 } // namespace Dali::TextAbstraction::Internal