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